Вопрос

Мне показалось довольно странным следующее.Опять же, я в основном использовал замыкания в динамических языках, которые не должны вызывать подозрений в одной и той же «ошибке».Компилятор недоволен следующим:

VoidFunction t = delegate { int i = 0; };

int i = 1;

Там говорится:

Локальная переменная по имени «Я» не может быть объявлена ​​в этом объеме, потому что она придаст другим значению «I», который уже используется в «детской» сфере, чтобы обозначить что -то другое

По сути, это означает, что переменные, объявленные внутри делегата, будут иметь область действия объявленной функции.Не совсем то, чего я ожидал.Я даже не пытался вызвать эту функцию.По крайней мере, в Common Lisp есть возможность сказать, что переменная должна иметь динамическое имя, если вы действительно хотите, чтобы она была локальной.Это особенно важно при создании макросов, которые не утекают, но что-то подобное будет полезно и здесь.

Итак, мне интересно, что делают другие люди, чтобы обойти эту проблему?

Чтобы уточнить, я ищу решение, в котором переменные, которые я объявляю в делегате, не мешают объявленным переменным. после делегат.И я хочу иметь возможность захватывать переменные, объявленные перед делегатом.

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

Решение

Это должно быть таким образом, чтобы позволить анонимным методам (и лямбда-выражениям) использовать локальные переменные и параметры, находящиеся в области содержащего метода.

Обходной путь — либо использовать разные имена для переменной, либо создать обычный метод.

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

«Замыкание», создаваемое анонимной функцией, несколько отличается от того, что создается в других динамических языках (в качестве примера я буду использовать Javascript).

function thing() {
    var o1 = {n:1}
    var o2 = {dummy:"Hello"}
    return function() { return o1.n++; }
}

var fn = thing();
alert(fn());
alert(fn());

Этот небольшой фрагмент JavaScript отобразит 1, затем 2.Анонимная функция может получить доступ к переменной o1, поскольку она существует в ее цепочке областей действия.Однако анонимная функция имеет полностью независимую область видимости, в которой она может создать еще одну переменную o1 и тем самым скрыть любую другую переменную, расположенную дальше по цепочке областей видимости.Также обратите внимание, что все переменные во всей цепочке остаются, следовательно, o2 будет продолжать существовать, удерживая ссылку на объект, пока переменная fn содержит ссылку на функцию.

Теперь сравните с анонимными функциями C#: -

class C1 { public int n {get; set;} }
class C2 { public string dummy { get; set; } }

Func<int> thing() {
   var o1 = new C1() {n=1};
   var o2 = new C2() {dummy="Hello"};
   return delegate { return o1.n++; };
}
...
Func<int> fn = thing();
Console.WriteLine(fn());
Console.WriteLine(fn());

В этом случае анонимная функция не создает действительно независимую область действия, как и объявление переменной в любом другом блоке кода внутри функции { } (используемом в foreach, if, и т. д.)

Следовательно, применяются те же правила: код вне блока не может получить доступ к переменным, объявленным внутри блока, но вы также не можете повторно использовать идентификатор.

Замыкание создается, когда анонимная функция передается за пределы функции, в которой она была создана.Отличие от примера Javascript заключается в том, что останутся только те переменные, которые фактически используются анонимной функцией, следовательно, в этом случае объект, хранящийся в o2, будет доступен для GC, как только все завершится,

Вы также получите CS0136 из такого кода:

  int i = 0;
  if (i == 0) {
    int i = 1;
  }

Область действия второго объявления «i» однозначна, в таких языках, как C++, с этим нет никаких проблем.Но разработчики языка C# решили это запретить.Учитывая приведенный выше фрагмент, думаете ли вы, что это была плохая идея?Добавьте кучу дополнительного кода, и вы сможете какое-то время смотреть на этот код и не видеть ошибки.

Обходной путь тривиальный и безболезненный: просто придумайте другое имя переменной.

Это потому, что делегат может ссылаться на переменные вне делегата:

int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };

Если я правильно помню, компилятор создает член класса внешних переменных, на которые ссылается анонимный метод, чтобы это работало.

Вот обходной путь:

class Program
    {
        void Main()
        {
            VoidFunction t = RealFunction;
            int i = 1;
        }
        delegate void VoidFunction();
        void RealFunction() { int i = 0; }
    } 

На самом деле ошибка, похоже, не имеет ничего общего с анонимными делегатами или лямбда-выражениями.Если вы попытаетесь скомпилировать следующую программу...

using System;

class Program
{
    static void Main()
    {
        // Action t = delegate
        {
            int i = 0;
        };

        int i = 1;
    }
}

...вы получаете точно такую ​​же ошибку, независимо от того, комментируете ли вы строку или нет.А помощь по ошибке показан очень похожий случай.Я думаю, что разумно запретить оба случая на том основании, что программисты могут перепутать две переменные.

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