web-dev-qa-db-fra.com

Accès à la fermeture modifiée

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
}

Ce qui précède semble bien fonctionner, bien que ReSharper se plaint de "l'accès à la fermeture modifiée". Quelqu'un peut-il faire la lumière sur cette question?

(ce sujet a continué ici )

305
Vyas Bharghava

Dans ce cas, ça va, puisque vous exécutez le délégué dans la boucle.

Si vous enregistrez et utilisez le délégué ultérieurement, vous constaterez que tous les délégués lèveraient des exceptions lors de la tentative d'accès aux fichiers [i] - ils capturent la variable i plutôt que sa valeur lors de la création des délégués.

En bref, il faut en prendre conscience comme un piège potentiel , mais dans ce cas cela ne vous fait pas de mal.

Voir le bas de cette page pour un exemple plus complexe où les résultats sont contre-intuitifs.

306
Jon Skeet

Je sais que la question est ancienne, mais j’ai récemment étudié les fermetures d’opérations et j’ai pensé qu’un échantillon de code pourrait être utile. En coulisse, le compilateur génère une classe qui représente une clôture lexicale pour votre appel de fonction. Cela 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 car les prédicats sont invoqués immédiatement après la 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 deviez stocker et ensuite invoquer les prédicats, vous verriez que chaque appel aux prédicats appellerait réellement la même méthode sur la même instance de la classe de fermeture et utiliserait donc la même valeur pour je.

28
gerrard00

"fichiers" est une variable externe capturée , car elle a été capturée par la fonction de délégué anonyme. Sa durée de vie est prolongée par la fonction de délégué anonyme.

Variables externes capturées Lorsqu'une variable externe est référencée par une fonction anonyme, la variable externe est dite capturée par la fonction anonyme. En règle générale, la durée de vie d'une variable locale est limitée à l'exécution du bloc ou de l'instruction auquel elle est associée (variables locales). Toutefois, la durée de vie d'une variable externe capturée est prolongée au moins jusqu'à ce que l'arborescence des délégués ou des expressions créée à partir de la fonction anonyme soit éligible pour le garbage collection.

Variables extérieures sur MSDN

Lorsqu'une variable locale ou un paramètre de valeur est capturé par une fonction anonyme, la variable ou le paramètre local n'est plus considérée comme une variable fixe (variables fixes et mobiles), mais plutôt comme une variable mobile. Ainsi, tout code non sécurisé prenant l'adresse d'une variable externe capturée doit d'abord utiliser l'instruction fixed pour corriger la variable. Notez que contrairement à une variable non capturée, une variable locale capturée peut être simultanément exposée à plusieurs threads d'exécution.

2
chris hu