web-dev-qa-db-ja.com

奇数の戻り構文ステートメント

これは奇妙に聞こえるかもしれませんが、インターネットでこの構文を検索する方法すら知らず、正確に何を意味するのかもわかりません。

だから私はいくつかのMoreLINQコードを見守った後、このメソッドに気づいた

_public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));

    return _(); IEnumerable<TSource> _()
    {
        var knownKeys = new HashSet<TKey>(comparer);
        foreach (var element in source)
        {
            if (knownKeys.Add(keySelector(element)))
                yield return element;
        }
    }
}
_

この奇妙なreturnステートメントとは何ですか? return _();

102
kuskmen

これは、ローカル関数をサポートするC#7.0です。..

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
       this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new 
           ArgumentNullException(nameof(source));
        if (keySelector == null) throw 
             new ArgumentNullException(nameof(keySelector));

        // This is basically executing _LocalFunction()
        return _LocalFunction(); 

        // This is a new inline method, 
        // return within this is only within scope of
        // this method
        IEnumerable<TSource> _LocalFunction()
        {
            var knownKeys = new HashSet<TKey>(comparer);
            foreach (var element in source)
            {
                if (knownKeys.Add(keySelector(element)))
                    yield return element;
            }
        }
    }

Func<T>を使用した現在のC#

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(
       this IEnumerable<TSource> source,
        Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
    {
        if (source == null) throw new 
           ArgumentNullException(nameof(source));
        if (keySelector == null) throw 
             new ArgumentNullException(nameof(keySelector));

        Func<IEnumerable<TSource>> func = () => {
            var knownKeys = new HashSet<TKey>(comparer);
            foreach (var element in source)
            {
                if (knownKeys.Add(keySelector(element)))
                    yield return element;
            }
       };

        // This is basically executing func
        return func(); 

    }

トリックは、_()が使用後に宣言されることです。これはまったく問題ありません。

ローカル関数の実際の使用

上記の例は、インラインメソッドの使用方法のデモにすぎませんが、メソッドを1回だけ呼び出す場合はほとんど役に立ちません。

しかし、上記の例では、PhoshiおよびLuaanによるコメントで述べたように、ローカル関数を使用する利点があります。 yield returnを使用した関数は、誰かが反復しない限り実行されないため、この場合、ローカル関数の外側のメソッドが実行され、誰も値を反復しなくてもパラメーター検証が実行されます。

メソッドで何度もコードを繰り返してきたので、この例を見てみましょう。

  public void ValidateCustomer(Customer customer){

      if( string.IsNullOrEmpty( customer.FirstName )){
           string error = "Firstname cannot be empty";
           customer.ValidationErrors.Add(error);
           ErrorLogger.Log(error);
           throw new ValidationError(error);
      }

      if( string.IsNullOrEmpty( customer.LastName )){
           string error = "Lastname cannot be empty";
           customer.ValidationErrors.Add(error);
           ErrorLogger.Log(error);
           throw new ValidationError(error);
      }

      ... on  and on... 
  }

これを最適化できます...

  public void ValidateCustomer(Customer customer){

      void _validate(string value, string error){
           if(!string.IsNullOrWhitespace(value)){

              // i can easily reference customer here
              customer.ValidationErrors.Add(error);

              ErrorLogger.Log(error);
              throw new ValidationError(error);                   
           }
      }

      _validate(customer.FirstName, "Firstname cannot be empty");
      _validate(customer.LastName, "Lastname cannot be empty");
      ... on  and on... 
  }
106
Akash Kava

より単純な例を考えてみましょう

_void Main()
{
    Console.WriteLine(Foo()); // Prints 5
}

public static int Foo()
{
    return _();

    // declare the body of _()
    int _()
    {
        return 5;
    }
}
_

_()は、returnステートメントを含むメソッド内で宣言されたローカル関数です。

24
Stuart