2011-04-02 13 views
6
public virtual void OnRegistrationJoin(RegistrationJoinEventArgs e) 
{ 
    foreach (Mobile member in e.Team) 
    { 
     member.SendMessage(1161, "You join the {0}.", EventFullName); 

     if (e.Team.Count > 1) 
     { 
      Joinees.Remove(member); 
      member.SendMessage(1161, "Your team formation is:"); 

      int i = 0; 

      foreach (Mobile parter in e.Team.Where(partner => partner != member).ToList()) 
      { 
       member.SendMessage(1150, "{0}: {1}.", ++i, partner.Name); 
      } 
     } 
    } 

    Members.Add(e.Team); 
} 

Recibo una advertencia de "acceso al cierre modificado" por reafilado, me preguntaba ¿qué hay de malo en este código, ya que todo lo que hago en el ciclo interno es enviar un mensaje?C# Acceso al cierre modificado

+1

Posible duplicado de [Acceso al cierre modificado] (http://stackoverflow.com/questions/235455/access-to-modified-closure) y varios otros. – adrianbanks

Respuesta

13

El problema está en:

e.Team.Where(partner => partner != member) 

La variable member es una referencia directa a la variable member en el alcance externo. Si bien es posible que no tenga un problema con esto en el código anterior, es problemático cuando está ejecutando el código en varios subprocesos o si no está evaluando el lambda en el método Where de inmediato (por ejemplo, usando IQueryable en lugar de IEnumerable)

La razón por la cual este es un problema es que C# genera un método para luego pasar como un delegado a Where. Ese método necesita acceso directo al memeber. Si se va a asignar la referencia a otra variable como esto:

var m = member; 
// ... 
e.Team.Where(partner => partner != m); 

Entonces C# puede "capturar" ese valor en un constructo llamado "cierre" y pasarlo al método generado. Esto asegurará que cuando member cambia, el valor que espera que sea cuando lo pasa a Where no se cambia.

+0

+1: En última instancia, los problemas más grandes que veo que salen de esto para la mayoría de la gente son la ejecución diferida. – vcsjones

+0

Se arregla fácilmente haciendo var closureMember = member. Y usando closureMember en el LINQ. –

+0

@Femaref - pero es la referencia cambiante el problema, no la instancia cambiante. – codekaizen

0

supongo member.SendMessage podría modificar member

Eso está modificando una variable cerrado que se use en el lambda

+0

No realmente. El problema es el bucle foreach externo que modifica la variable 'miembro' encuadernada en el lambda. – Femaref

2

El resharper de partes se refiere a e.Team.Where(partner => partner != member).ToList(), como se ha cambiado member. En este caso, esto no es un problema, pero en otros casos, esto puede ser un problema.

Nota: no es necesario utilizar ToList(), lo que obliga a una evaluación ansiosa del IEnumerable<T>. Simplemente itere sobre e.Team.Where(partner => partner != member).

Cuestiones relacionadas