Question

J'ai trouvé ce qui suit plutôt étrange. Là encore, j'ai principalement utilisé des fermetures dans des langages dynamiques qui ne devraient pas être suspects du même "bogue". Ce qui suit rend le compilateur malheureux:

VoidFunction t = delegate { int i = 0; };

int i = 1;

Il est écrit:

  

Une variable locale nommée 'i' ne peut pas être   déclaré dans cette portée car il   donnerait un sens différent à «i»,   qui est déjà utilisé chez un 'enfant'   champ d'application pour indiquer autre chose

Cela signifie donc que les variables déclarées dans un délégué auront la portée de la fonction déclarée dans. Pas exactement ce à quoi je m'attendais. Je n'ai même pas essayé d'appeler la fonction. Common Lisp a au moins une fonctionnalité dans laquelle vous dites qu'une variable doit avoir un nom dynamique, si vous voulez vraiment qu'elle soit locale. Ceci est particulièrement important lors de la création de macros qui ne fuient pas, mais quelque chose comme cela serait utile ici aussi.

Je me demande donc ce que les autres personnes font pour résoudre ce problème?

Pour clarifier, je cherche une solution dans laquelle les variables que je déclare dans le délégué n'interfèrent pas avec les variables déclarées après par le délégué. Et je veux toujours pouvoir capturer les variables déclarées avant le délégué.

Était-ce utile?

La solution

Cela doit être ainsi que les méthodes anonymes (et les lambdas) puissent utiliser des variables et des paramètres locaux définis dans la méthode conteneur.

Les solutions de contournement consistent à utiliser des noms différents pour la variable ou à créer une méthode ordinaire.

Autres conseils

La " fermeture " créé par une fonction anonyme est quelque peu différent de celui créé dans d'autres langages dynamiques (je vais utiliser Javascript comme exemple).

function thing() {
    var o1 = {n:1}
    var o2 = {dummy:"Hello"}
    return function() { return o1.n++; }
}

var fn = thing();
alert(fn());
alert(fn());

Ce petit morceau de javascript affichera 1 puis 2. La fonction anonyme peut accéder à la variable o1 car elle existe dans sa chaîne de domaines. Cependant, la fonction anonyme a une portée entièrement indépendante dans laquelle elle pourrait créer une autre variable o1 et masquer ainsi toute autre variable plus loin dans la chaîne de la portée. Notez également que toutes les variables de la chaîne entière restent, donc o2 continuerait d'exister en conservant une référence d'objet tant que la variable fn conservera la référence de fonction.

Comparez maintenant avec les fonctions anonymes C #: -

class C1 { public int n {get; set;} }
class C2 { public string dummy { get; set; } }

Func<int> thing() {
   var o1 = new C1() {n=1};
   var o2 = new C2() {dummy="Hello"};
   return delegate { return o1.n++; };
}
...
Func<int> fn = thing();
Console.WriteLine(fn());
Console.WriteLine(fn());

Dans ce cas, la fonction anonyme ne crée pas d'étendue vraiment indépendante, pas plus que la déclaration de variable dans un autre bloc de code en fonction {} serait (utilisée dans un foreach , si , etc.)

Par conséquent, les mêmes règles s'appliquent, le code en dehors du bloc ne peut pas accéder aux variables déclarées à l'intérieur du bloc, mais vous ne pouvez pas non plus réutiliser un identifiant.

Une fermeture est créée lorsque la fonction anonyme est transmise en dehors de la fonction dans laquelle elle a été créée. La variation par rapport à l'exemple Javascript est que seules les variables réellement utilisées par la fonction anonyme resteront. Par conséquent, l'objet détenu par o2 sera disponible pour GC dès que tout sera terminé,

Vous obtiendrez également le code CS0136 à partir du code suivant:

  int i = 0;
  if (i == 0) {
    int i = 1;
  }

Portée de la 2e déclaration de "i" est sans ambiguïté, les langages comme le C ++ n’ont aucun intérêt particulier. Mais les concepteurs du langage C # ont décidé de l'interdire. Compte tenu de l'extrait ci-dessus, pensez-vous toujours que c'était une mauvaise idée? Ajoutez une poignée de code supplémentaire et vous pourrez regarder ce code pendant un moment sans voir le bogue.

La solution de contournement est simple et sans tracas. Créez simplement un nom de variable différent.

C'est parce que le délégué peut référencer des variables en dehors du délégué:

int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };

Si je me souviens bien, le compilateur crée un membre de la classe avec les variables externes référencées dans la méthode anonyme, afin que cela fonctionne.

Voici une solution de contournement:

class Program
    {
        void Main()
        {
            VoidFunction t = RealFunction;
            int i = 1;
        }
        delegate void VoidFunction();
        void RealFunction() { int i = 0; }
    } 

En fait, l'erreur ne semble pas avoir de lien avec les délégués anonymes ou les expressions lamda. Si vous essayez de compiler le programme suivant ...

using System;

class Program
{
    static void Main()
    {
        // Action t = delegate
        {
            int i = 0;
        };

        int i = 1;
    }
}

... vous obtenez exactement la même erreur, que vous commentiez ou non dans la ligne. La aide sur les erreurs affiche un cas très similaire. Je pense qu'il est raisonnable de rejeter les deux cas au motif que les programmeurs pourraient confondre les deux variables.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top