8
votes

C # Accès à la fermeture modifiée

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);
}
I get "access to modified closure" warning by resharper, I was wondering what's so wrong with this code, since all I do in the inner loop is send a message?

1 commentaires

Dupliqué possible de Accès à la fermeture modifiée et plusieurs autres.


3 Réponses :


0
votes

Je suppose membre.SENDMessage pourrait modifier membre

qui modifie une variable fermée utilisée dans la Lambda


1 commentaires

Pas vraiment. Le problème est la boucle de foresach externe qui modifie la variable de la variable dans la Lambda.



14
votes

Le problème est dans: xxx

la variable membre est une référence directe à la variable de la variable dans la portée extérieure. Pendant que vous n'avez peut-être pas de problème avec ceci dans le code ci-dessus, il est problématique lorsque vous exécutez le code sur plusieurs threads ou si vous n'évaluez pas la Lambda dans le méthode (pour Exemple, en utilisant iquéryable au lieu de iEnumerable ).

La raison en est un problème est que c # génère une méthode pour ensuite transmettre comme délégué à < code> où . Cette méthode a besoin d'un accès direct à memeber . Si vous deviez attribuer la référence à une autre variable comme celle-ci: xxx

puis c # peut "capturer" cette valeur dans une construction appelée "fermeture" et transmettez-la au généré méthode. Cela garantira que lorsque membre modifie la valeur que vous vous attendez à ce que ce soit lorsque vous le transmettez à n'est pas modifié.


6 commentaires

+1: En fin de compte, les problèmes les plus importants que je vois pour la plupart des gens sont différés d'exécution.


Facilement corrigé en faisant Var Closuremembernember = membre. Et en utilisant des closeumember dans la linq.


@Femarf - mais c'est l'évolution de la référence qui est le problème, pas l'instance changeante.


Bien sûr que c'est. Mais le comportement de votre Closuremember sera exactement la même - chaque itération, elle passera à l'objet actuel. S'il n'utilisait pas la requête directement mais différée, seul le dernier membre de la séquence de Foreach'ed serait utilisé.


Oh, vous vouliez dire avoir attribuer ClosEremember en dehors de la Lambda. Bien sûr, cela fonctionnera. Désolé.


Merci pour l'explication, pouvez-vous éditer avec un exemple de cas simple où cela deviendrait un problème?



2
votes

La pièce Resharper se plaint est e.team.where (partenaire => partenaire! = membre) .tolist () , comme le membre référencé sera modifié. Dans ce cas, ce n'est pas un problème, mais dans d'autres cas, cela peut être un problème.

Remarque: vous n'avez pas à utiliser tolist () , qui oblige l'évaluation désireuse du ienumerable . Tout simplement itérer sur e.team.where (partenaire => partenaire! = Membre) .


0 commentaires