web-dev-qa-db-fra.com

Déclaration de syntaxe de retour impair

Je sais que cela peut paraître étrange, mais je ne sais même pas comment rechercher cette syntaxe sur Internet et je ne suis pas sûr de ce que signifie exactement.

J'ai donc surveillé du code MoreLINQ, puis j'ai remarqué cette méthode.

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;
        }
    }
}

Quelle est cette déclaration de retour étrange? return _(); ?

102
kuskmen

C'est C # 7.0 qui supporte les fonctions locales ....

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;
            }
        }
    }

C # actuel avec Func<T>

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(); 

    }

L'astuce est que _ () est déclaré après son utilisation, ce qui est parfaitement correct.

tilisation pratique des fonctions locales

L'exemple ci-dessus est juste une démonstration de l'utilisation de la méthode inline, mais il est fort probable que si vous appelez la méthode une seule fois, elle est alors inutile.

Mais dans l'exemple ci-dessus, comme mentionné dans les commentaires de Phoshi et Luaan, l'utilisation de la fonction locale présente un avantage. Etant donné que la fonction avec return return ne sera pas exécutée à moins que quelqu'un ne l'itère, dans ce cas, la méthode en dehors de la fonction locale sera exécutée et la validation du paramètre sera effectuée même si personne n'itère la valeur.

Plusieurs fois, nous avons répété le code dans la méthode, regardons cet exemple ..

  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... 
  }

Je pourrais optimiser cela avec ...

  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

Prenons l'exemple plus simple

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

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

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

_() est une fonction locale déclarée dans la méthode contenant l'instruction return.

24
Stuart