web-dev-qa-db-fra.com

Fonction locale vs Lambda C # 7.0

Je regarde les nouvelles implémentations dans C # 7.0 et je trouve intéressant qu’elles aient implémenté des fonctions locales mais je ne peux imaginer un scénario dans lequel une fonction locale serait préférée à une expression lambda la différence entre les deux.

Je comprends que les lambda sont des fonctions anonymous alors que les fonctions locales ne le sont pas, mais je ne peux pas imaginer de scénario réel où la fonction locale présente des avantages par rapport aux expressions lambda. 

Tout exemple serait très apprécié. Merci.

133
Sid

Cela a été expliqué par Mads Torgersen dans C # Design Meeting Notes où les fonctions locales ont été abordées pour la première fois :

Vous voulez une fonction d'assistance. Vous ne l'utilisez que dans une seule fonction et il est probable que des variables et des paramètres de type se trouvent dans la portée de cette fonction. D'un autre côté, contrairement à un lambda, vous n'en avez pas besoin en tant qu'objet de première classe. Vous ne souhaitez donc pas lui attribuer un type de délégué et allouer un objet délégué réel. Vous pouvez également vouloir qu'il soit récursif ou générique, ou l'implémenter en tant qu'itérateur.

Pour en développer davantage, les avantages sont les suivants:

  1. Performance.

    Lors de la création d'un lambda, un délégué doit être créé, ce qui est une allocation inutile dans ce cas. Les fonctions locales ne sont en réalité que des fonctions, aucun délégué n'est nécessaire.

    De plus, les fonctions locales sont plus efficaces avec la capture de variables locales: lambdas capture généralement les variables dans une classe, tandis que les fonctions locales peuvent utiliser une structure (transmise avec ref), ce qui évite encore une allocation.

    Cela signifie également que les fonctions locales sont moins chères et qu'elles peuvent être en ligne, ce qui permet d’augmenter encore les performances.

  2. Les fonctions locales peuvent être récursives.

    Lambdas peut aussi être récursif, mais il nécessite un code peu pratique, dans lequel vous affectez d'abord null à une variable déléguée, puis le lambda. Les fonctions locales peuvent naturellement être récursives (y compris mutuellement récursives).

  3. Les fonctions locales peuvent être génériques.

    Les Lambdas ne peuvent pas être génériques, car elles doivent être affectées à une variable de type concret (ce type peut utiliser des variables génériques à partir de la portée externe, mais ce n'est pas la même chose).

  4. Les fonctions locales peuvent être implémentées en tant qu'itérateur.

    Lambdas ne peut pas utiliser le mot clé yield return (et yield break) pour implémenter la fonction IEnumerable<T>-. Les fonctions locales peuvent.

  5. Les fonctions locales ont une meilleure apparence.

    Cela n'est pas mentionné dans la citation ci-dessus et pourrait n'être que mon biais personnel, mais je pense que la syntaxe d'une fonction normale est préférable à l'attribution d'un lambda à une variable déléguée. Les fonctions locales sont également plus succinctes.

    Comparer:

    int add(int x, int y) => x + y;
    Func<int, int, int> add = (x, y) => x + y;
    
224
svick

En plus de la réponse géniale de svick , les fonctions locales présentent un avantage supplémentaire:
Ils peuvent être définis n’importe où dans la fonction, même après l’instruction return

public double DoMath(double a, double b)
{
    var resultA = f(a);
    var resultB = f(b);
    return resultA + resultB;

    double f(double x) => 5 * x + 3;
}
65
Tim Pohlmann

Si vous vous demandez également comment tester la fonction locale, vous devez vérifier JustMock car il a la fonctionnalité pour le faire. Voici un exemple de classe simple qui sera testé:

public class Foo // the class under test
{ 
    public int GetResult() 
    { 
        return 100 + GetLocal(); 
        int GetLocal () 
        { 
            return 42; 
        } 
    } 
}
 </ pre> 

</ code> Et voici à quoi ressemble le test:

[TestClass] 
public class MockLocalFunctions 
{ 
    [TestMethod] 
    public void BasicUsage() 
    { 
        //Arrange 
        var foo = Mock.Create<Foo>(Behavior.CallOriginal); 
        Mock.Local.Function.Arrange<int>(foo, "GetResult", "GetLocal").DoNothing(); 

        //Act 
        var result = foo. GetResult(); 

        //Assert 
        Assert.AreEqual(100, result); 
    } 
} 
 </ pre> 

</ code> Voici un lien vers JustMock documentation .

Avertissement. Je suis l'un des développeurs responsables de JustMock .

2
Mihail Vladov

J'utilise des fonctions en ligne pour éviter la pression de récupération des ordures, en particulier lorsque les méthodes d'exécution sont longues. Supposons que l’on souhaite obtenir 2 ans ou les données du marché pour un symbole boursier donné. En outre, on peut avoir beaucoup de fonctionnalités et de logique métier si nécessaire.

ce que l'on fait est d'ouvrir une connexion de socket au serveur et de boucler les données en liant un événement à un événement. On peut y penser de la même manière qu’une classe est conçue, une seule n’écrit pas des méthodes helper partout qui ne fonctionnent réellement que pour une seule pièce de fonctionnalité. Ci-dessous quelques exemples de ce à quoi cela pourrait ressembler, veuillez noter que j’utilise des variables et que les méthodes "helper" sont en-dessous de la méthode finally. Enfin, je supprime joliment les gestionnaires d'événements, si ma classe Exchange était externe/injectée, aucun gestionnaire d'événements en attente ne serait enregistré.

void List<HistoricalData> RequestData(Ticker ticker, TimeSpan timeout)
{
    var socket= new Exchange(ticker);
    bool done=false;
    socket.OnData += _onData;
    socket.OnDone += _onDone;
    var request= NextRequestNr();
    var result = new List<HistoricalData>();
    var start= DateTime.Now;
    socket.RequestHistoricalData(requestId:request:days:1);
    try
    {
      while(!done)
      {   //stop when take to long….
        if((DateTime.Now-start)>timeout)
           break;
      }
      return result;

    }finally
    {
        socket.OnData-=_onData;
        socket.OnDone-= _onDone;
    }


   void _OnData(object sender, HistoricalData data)
   {
       _result.Add(data);
   }
   void _onDone(object sender, EndEventArgs args)
   {
      if(args.ReqId==request )
         done=true;
   } 
}

Vous pouvez voir les avantages mentionnés ci-dessous, ici vous pouvez voir un exemple de mise en œuvre. J'espère que cela aide à expliquer les avantages.