2010-12-02 5 views
1

Je ne peux pas expliquer un problème que j'ai rencontré. Fondamentalement, je reçois une réponse différente si j'utilise la syntaxe lambda dans une boucle foreach que si je l'utilise dans une boucle for. Dans le code ci-dessous, j'inscris un délégué dans une classe "répartiteur". J'enveloppe ensuite le délégué à la sortie dans un autre délégué et retourne une liste de ces délégués emballés. Je les exécute ensuite. La sortie attendue de l'exécution de la liste des fonctions enveloppées est 1,2. Cependant je ne le vois pas quand je combine un lambda et une boucle foreach.Différents comportements avec une boucle for et une boucle foreach avec des fermetures

Ce n'est pas le code qui cause le problème, mais le cas le plus simple que je pourrais faire pour le reproduire. Je préférerais ne pas discuter des cas d'utilisation de cela, je suis plus curieux de savoir pourquoi j'obtiens un comportement auquel je ne m'attendais pas. Si j'utilise la boucle foreach ci-dessous avec la syntaxe lambda, elle échoue. Si j'utilise la nouvelle syntaxe Action() et que foreach fonctionne, si j'utilise la syntaxe lambda dans une boucle for, cela fonctionne. Quelqu'un peut-il expliquer ce qui se passe ici. Cela m'a vraiment perplexe.

public class Holder 
{ 
    public Holder(int ID, Dispatcher disp) 
    { 
     this.ID = ID; 
     disp.Register(Something); 
    } 
    public int ID { get; set; } 
    private void Something(int test) { Console.WriteLine(ID.ToString()); } 
} 

public class Dispatcher 
{ 
    List<Action<int>> m_Holder = new List<Action<int>>(); 

    public void Register(Action<int> func) 
    { 
     m_Holder.Add(func); 
    } 

    public List<Action<int>> ReturnWrappedList() 
    { 
     List<Action<int>> temp = new List<Action<int>>(); 

     //for (int i = 0; i < m_Holder.Count; i++)  //Works - gives 1, 2 
     //{ 
     // var action = m_Holder[i]; 
     // temp.Add(p => action(p)); 
     //} 

     foreach (var action in m_Holder) 
     { 
      temp.Add(p => action(p)); //Fails - gives 2,2 
      //temp.Add(new Action<int>(action)); Works - gives 1,2 
     } 

     return temp; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var disp = new Dispatcher(); 
     var hold1 = new Holder(1, disp); 
     var hold2 = new Holder(2, disp); 
     disp.ReturnWrappedList().ForEach(p => p(1)); 
    } 
} 

Répondre

0

Avez-vous essayé:

foreach (var action in m_Holder) 
{ 
    var a = action; 
    temp.Add(p => a(p)); 
} 
0

C'est la question classique d'une fermeture capturé avec une portée qui n'est pas ce que vous attendez. Dans la foreach, l'action a une portée externe, de sorte que l'exécution capture la valeur dernière de la boucle. Dans le cas de, vous créez l'action dans la portée interne, de sorte que la fermeture est supérieure à la valeur locale à chaque itération.