Вопрос

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
}

Кажется, что вышеописанное работает нормально, хотя ReSharper жалуется, что это «доступ к модифицированному замыканию».Может ли кто-нибудь пролить свет на это?

(продолжение этой темы здесь)

Это было полезно?

Решение

В этом случае все в порядке, поскольку вы фактически выполняете делегат в цикле.

Если вы сохраните делегат и будете использовать его позже, вы обнаружите, что все делегаты выдают исключения при попытке доступа к файлам [i] - они захватывают переменную i , а не его значение во время создания делегата.

Короче говоря, это то, что нужно осознавать как потенциальную ловушку, но в этом случае это не повредит вам.

См. нижнюю часть этой страницы для более сложного примера, где результаты нелогичны.

Другие советы

Я знаю, что это старый вопрос, но недавно я изучал замыкания и подумал, что пример кода может быть полезен. За кулисами компилятор генерирует класс, который представляет лексическое замыкание для вашего вызова функции. Вероятно, это выглядит примерно так:

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

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

Как упоминалось выше, ваша функция работает, потому что предикаты вызываются сразу после создания. Компилятор сгенерирует что-то вроде:

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;
}

С другой стороны, если вы сохраните, а затем вызовете предикаты, вы увидите, что каждый отдельный вызов предикатов действительно будет вызывать один и тот же метод в одном и том же экземпляре класса замыкания и, следовательно, будет использовать то же значение для меня.

«файлы» — это захваченная внешняя переменная потому что он был захвачен анонимной функцией делегата.Его время жизни продлевается с помощью функции анонимного делегата.

Захваченные внешние переменные Когда анонимная функция ссылается на внешнюю переменную, внешняя переменная, как говорят, была захвачена анонимной функцией.Обычно время жизни локальной переменной ограничивается выполнением блока или инструкции, с которой она связана (локальные переменные).Однако время жизни захваченной внешней переменной продлевается как минимум до тех пор, пока дерево делегата или выражения, созданное из анонимной функции, не станет пригодным для сборки мусора.

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#outer-variables

Когда локальная переменная или параметр-значение захватывается анонимной функцией, локальная переменная или параметр больше не считается фиксированной переменной (фиксированные и перемещаемые переменные), а вместо этого считается перемещаемой переменной.Таким образом, любой небезопасный код, принимающий адрес захваченной внешней переменной, должен сначала использовать оператор fix для исправления переменной.Обратите внимание, что в отличие от незахваченной переменной, захваченная локальная переменная может быть одновременно доступна нескольким потокам выполнения.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top