323 votes

L'accès à l'Modifié Fermeture

string [] files = new string[2];
files[0] = "ThinkFarAhead.Example.Settings.Configuration_Local.xml";
files[1] = "ThinkFarAhead.Example.Settings.Configuration_Global.xml";

//Resharper complains this is an "access to modified closure"
for (int i = 0; i < files.Length; i++ )
{
    // Resharper disable AccessToModifiedClosure
    if(Array.Exists(Assembly.GetExecutingAssembly().GetManifestResourceNames(),
    delegate(string name) { return name.Equals(files[i]); }))
         return Assembly.GetExecutingAssembly().GetManifestResourceStream(files[i]);
    // ReSharper restore AccessToModifiedClosure
}

Le ci-dessus semble bien fonctionner si ReSharper se plaint que c'est "l'accès à l'modifié fermeture". Quelqu'un peut-il éclairer sur ce point?

(à ce sujet, suite ici)

319voto

Jon Skeet Points 692016

Dans ce cas, c'est bien, puisque vous êtes l'exécuter réellement le délégué à l'intérieur de la boucle.

Si vous aviez sauvegardé le délégué et l'utiliser plus tard, cependant, vous trouverez que tous les délégués lancer des exceptions lorsque vous essayez d'accéder aux fichiers[i] - ils de la capture de la variable i plutôt que sa valeur au moment de l'délégués de la création.

En bref, c'est quelque chose d'être conscient de comme un potentiel piège, mais dans ce cas, il ne fait pas de mal de vous.

Voir le bas de cette page pour un exemple plus complexe, où les résultats sont contraires à l'intuition.

30voto

Gerrard Lindsay Points 189

Je sais que c'est une vieille question, mais j'ai récemment été l'étude de fermetures et de la pensée un exemple de code pourrait être utile. En coulisses, le compilateur génère une classe qui représente un lexicale de fermeture pour votre appel de fonction. Il ressemble probablement quelque chose comme:

private sealed class Closure
{
    public string[] files;
    public int i;

    public bool YourAnonymousMethod(string name)
    {
        return name.Equals(this.files[this.i]);
    }
}

Comme mentionné ci-dessus, votre fonction fonctionne parce que les prédicats sont appelés immédiatement après leur création. Le compilateur va générer quelque chose comme:

private string Works()
{
    var closure = new Closure();

    closure.files = new string[3];
    closure.files[0] = "notfoo";
    closure.files[1] = "bar";
    closure.files[2] = "notbaz";

    var arrayToSearch = new string[] { "foo", "bar", "baz" };

    //this works, because the predicates are being executed during the loop
    for (closure.i = 0; closure.i < closure.files.Length; closure.i++)
    {
        if (Array.Exists(arrayToSearch, closure.YourAnonymousMethod))
            return closure.files[closure.i];
    }

    return null;
}

D'autre part, si vous avez été à la boutique et puis plus tard invoquer les prédicats, vous verrez que chaque appel à la prédicats serait vraiment l'appel de la même méthode sur la même instance de la fermeture de la classe et, par conséquent, serait d'utiliser la même valeur pour je.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X