Portée des variables dans un délégué

voix
8

J'ai trouvé ce qui suit assez étrange. Là encore, j'ai surtout utilisé les fermetures dans les langages dynamiques qui ne devrait pas être suspectable au même « bug ». Ce qui suit rend malheureux le compilateur:

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

int i = 1;

Ça dit:

Une variable locale appelée « i » ne peut pas être déclaré dans ce champ, car cela donnerait un sens différent de « i », qui est déjà utilisé dans un champ d'enfant »pour désigner autre chose

Donc, cela signifie essentiellement que les variables déclarées à l'intérieur d'un délégué auront la portée de la fonction déclarée. Pas exactement ce que je me serais attendu. Je havn't même essayé d'appeler la fonction. Au moins Common Lisp a une fonction où vous dire qu'une variable doit avoir un nom dynamique, si vous voulez vraiment être local. Ceci est particulièrement important lors de la création de macros qui ne coulent pas, mais quelque chose comme ça serait utile ici.

Je me demande ce que les autres font pour contourner ce problème?

Pour clarifier , je suis à la recherche d'une solution dans laquelle les variables que je déclare dans le Delegete ne pas interférer avec les variables déclarées après le délégué. Et je veux être encore en mesure de saisir les variables déclarées avant le délégué.

Créé 01/01/2009 à 13:23
source utilisateur
Dans d'autres langues...                            


6 réponses

voix
9

Il doit être de cette façon de permettre des méthodes anonymes (et lambdas) d'utiliser des variables locales et les paramètres scope dans la méthode contenant.

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

Créé 01/01/2009 à 13:33
source utilisateur

voix
3

La « fermeture » créée par une fonction anonyme est un 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 alors 1 2. La fonction anonyme peut accéder à la variable o1 parce qu'il existe sur sa chaîne de portée. Cependant, la fonction anonyme a une portée entièrement indépendante où elle pourrait créer une autre variable o1 et ainsi cacher tout autre en aval de la chaîne de portée. Notez également que demeurent, par conséquent o2 se poursuivre toutes les variables de toute la chaîne d'exister maintenant une référence d'objet pour aussi longtemps que le varialbe fn détient la référence de fonction.

Maintenant, comparez avec C # fonctions anonymes: -

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 une portée plus indépendante truely de déclaration variable dans tout autre en fonction {} bloc de code serait (utilisé dans un foreach, ifetc.)

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

Une fermeture est créé lorsque la fonction anonyme est passé en dehors de la fonction qu'il a été créé. La variation de l'exemple Javascript est que seules les variables réellement utilisées par la fonction anonyme restent, par conséquent, dans ce cas, l'objet détenu par o2 sera être disponible pour GC dès chose complète,

Créé 01/01/2009 à 14:26
source utilisateur

voix
1

Vous aurez également CS0136 de code comme ceci:

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

La portée de la 2e déclaration de « i » est sans ambiguïté, des langages comme C ++ ne pas de boeuf avec elle. Mais les concepteurs du langage C # a décidé de l'interdire. Compte tenu de l'extrait ci-dessus, pensez-vous toujours que était une mauvaise idée? Ajoutez à cela un tas de code supplémentaire et vous pouvez regarder ce code pendant un certain temps et ne pas voir le bug.

La solution de contournement est trivial et sans douleur, juste venir avec un nom de variable différente.

Créé 01/01/2009 à 14:56
source utilisateur

voix
0

En fait, l'erreur ne semble pas avoir quelque chose à voir avec les délégués anonymes ou des 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, peu importe si vous commentez dans la ligne ou non. L' aide d'erreur montre 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.

Créé 01/01/2009 à 14:57
source utilisateur

voix
0

Si je me souviens bien, le compilateur crée un membre de la classe des variables externes référencées dans la méthode anonyme, afin de rendre ce travail.

Voici une solution de contournement:

class Program
    {
        void Main()
        {
            VoidFunction t = RealFunction;
            int i = 1;
        }
        delegate void VoidFunction();
        void RealFunction() { int i = 0; }
    } 
Créé 01/01/2009 à 13:43
source utilisateur

voix
0

Il est parce que le délégué peut faire référence à des variables en dehors du délégué:

int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };
Créé 01/01/2009 à 13:34
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more