44 votes

Quelle est la durée de vie d'un délégué créé par un lambda en C#?

Les Lambdas sont gentils, car ils offrent des raisons de concision et de la localité et un formulaire supplémentaire de l'encapsulation. Au lieu d'avoir à écrire des fonctions qui ne sont utilisés que lorsque vous pouvez utiliser une lambda.

Tout en se demandant comment ils ont travaillé, j'ai intuitivement compris qu'ils sont probablement créée une seule fois. Cela m'a inspiré pour créer une solution qui permet de restreindre la portée d'un membre de classe au-delà de privé à un champ particulier en utilisant le lambda comme un identificateur de la portée, il a été créé en.

Cette application fonctionne, mais peut-être exagéré (encore qu'il recherche), prouver mon hypothèse.

Un petit exemple:

class SomeClass
{
    public void Bleh()
    {
        Action action = () => {};
    }

    public void CallBleh()
    {
        Bleh();  // `action` == {Method = {Void <SomeClass>b__0()}}
        Bleh();  // `action` still == {Method = {Void <SomeClass>b__0()}}
    }
}

Serait le lambda jamais renvoyer une nouvelle instance, ou est-il la garantie d'être toujours le même?

31voto

Jon Skeet Points 692016

Il n'est pas garanti de toute façon.

À partir de ce que je me souviens des cours de MS de mise en œuvre:

  • Une expression lambda qui ne tient pas compte de toutes les variables est mise en cache de manière statique
  • Une expression lambda qui ne saisit "cette" pourraient être capturés par exemple, mais n'est-ce pas
  • Une expression lambda qui capte une variable locale ne peut pas être mis en cache
  • Deux expressions lambda qui ont le même programme et le texte ne sont pas lissés; dans certains cas, ils pourraient être, mais le fait de travailler les situations dans lesquelles ils peuvent être serait très compliqué
  • EDIT: Comme Eric points dans les commentaires, vous devez également considérer le type des arguments d'être capturé par des méthodes génériques.

EDIT: Le texte pertinent de la C# 4 spec est dans la section 6.5.1:

Les Conversions des sémantiquement identiques anonyme des fonctions avec le même (éventuellement vide) de l'ensemble des capturé extérieur variable d'instances de la même délégué types sont autorisés (mais pas obligatoire) pour revenir à la même instance de délégué. Le terme sémantiquement identique est utilisé ici pour signifier que l'exécution de la fonction anonyme dans tous les cas, produire les mêmes effets, les mêmes arguments.

30voto

Eric Lippert Points 300275

Basé sur votre question ici et de ton commentaire de Jon répondre, je pense que vous confondez plusieurs choses. Pour s'assurer qu'elle est claire:

  • La méthode qui sauvegarde le délégué pour un lambda est toujours le même.
  • La méthode qui sauvegarde le délégué pour "le même" lambda qui apparaît lexicalement deux fois est autorisé à être le même, mais dans la pratique, n'est pas la même chose dans notre mise en œuvre.
  • Le délégué de l'instance qui est créé pour un lambda pourrait ou ne pourrait pas toujours être le même, selon la façon intelligente le compilateur est à propos de la mise en cache il.

Donc, si vous avez quelque chose comme:

for(i = 0; i < 10; ++i)
    M( ()=>{} )

puis chaque fois que M est appelé, vous obtenez la même instance du délégué parce que le compilateur est intelligent et génère

static void MyAction() {}
static Action DelegateCache = null;

...
for(i = 0; i < 10; ++i)
{
    if (C.DelegateCache == null) C.DelegateCache = new Action ( C.MyAction )
    M(C.DelegateCache);
}

Si vous avez

for(i = 0; i < 10; ++i)
    M( ()=>{this.Bar();} )

ensuite, le compilateur génère

void MyAction() { this.Bar(); }
...
for(i = 0; i < 10; ++i)
{
    M(new Action(this.MyAction));
}

Vous obtenez un nouveau délégué à chaque fois, avec la même méthode.

Le compilateur est autorisée (mais en fait non, pas cette fois) de générer des

void MyAction() { this.Bar(); }
Action DelegateCache = null;
...
for(i = 0; i < 10; ++i)
{
    if (this.DelegateCache == null) this.DelegateCache = new Action ( this.MyAction )
    M(this.DelegateCache);
}

Dans ce cas, vous obtenez toujours la même instance de délégué, si possible, et à tous les délégués seraient garantis par la même méthode.

Si vous avez

Action a1 = ()=>{};
Action a2 = ()=>{};

Ensuite, dans la pratique, le compilateur génère ce que

static void MyAction1() {}
static void MyAction2() {}
static Action ActionCache1 = null;
static Action ActionCache2 = null;
...
if (ActionCache1 == null) ActionCache1 = new Action(MyAction1);
Action a1 = ActionCache1;
if (ActionCache2 == null) ActionCache2 = new Action(MyAction2);
Action a2 = ActionCache2;

Cependant, le compilateur est permis de détecter que les deux lambdas sont identiques et de générer des

static void MyAction1() {}
static Action ActionCache1 = null;
...
if (ActionCache1 == null) ActionCache1 = new Action(MyAction1);
Action a1 = ActionCache1;
Action a2 = ActionCache1;

C'est que maintenant clair?

4voto

Jay Points 27907

Aucune garantie.

Une démonstration rapide:

Action GetAction()
{
    return () => Console.WriteLine("foo");
}

Appel deux fois, faire une ReferenceEquals(a,b), et vous obtiendrez true

Action GetAction()
{
    var foo = "foo";
    return () => Console.WriteLine(foo);
}

Appel deux fois, faire une ReferenceEquals(a,b), et vous obtiendrez false

3voto

Gregory A Beamer Points 10975

Je vois Skeet sauté alors que j'était en train de répondre, donc je ne vais pas y revenir ce point. Une chose que je voudrais suggérer, pour mieux comprendre comment vous utilisez des choses, est de se familiariser avec rétro-ingénierie d'outils et de IL. Prenons l'exemple de code(s) en question et désosser à l'IL. Il vous donnera une grande quantité d'informations sur le code de travail.

1voto

Michael Blackburn Points 471

Bonne question. Je n'ai pas de "réponse théorique," plus d'une réponse pratique: j'ai pu voir un compilateur optimisant le binaire à utiliser la même instance, mais je ne voudrais pas écrire de code suppose que c'est "garanti" à la même instance.

Je upvoted vous au moins, donc j'espère que quelqu'un peut vous donner la réponse théorique vous êtes à la recherche pour.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X