我发现以下内容相当奇怪。然后,我主要使用动态语言中的闭包,这对于相同的“bug”不应该是可疑的。以下内容使编译器不满意:

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

int i = 1;

它说:

  

名为“i”的局部变量不能   在此范围内声明,因为它   会给'我'一个不同的含义,   已经在“孩子”中使用   范围来表示别的东西

所以这基本上意味着在委托中声明的变量将具有声明的函数的范围。不完全是我所期望的。我甚至没有尝试过调用这个函数。至少Common Lisp有一个功能,你可以说变量应该有一个动态名称,如果你真的希望它是本地的。这在创建不泄漏的宏时尤其重要,但这样的东西在这里也会有所帮助。

所以我想知道其他人如何解决这个问题呢?

澄清我正在寻找一个解决方案,其中我在delegete中声明的变量不会干扰委托后声明的变量。我希望仍然能够捕获在委托之前声明的变量。

有帮助吗?

解决方案

必须允许匿名方法(和lambdas)在包含方法中使用作用域的局部变量和参数。

变通方法是为变量使用不同的名称,或者创建一个普通的方法。

其他提示

“封闭”。由匿名函数创建的与其他动态语言创建的有些不同(我将使用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变量,从而隐藏范围链中的任何其他变量。另请注意,整个链中的所有变量都保留,因此只要fn varialbe保存函数引用,o2就会继续存在,并保持对象引用。

现在与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 如果等。)

因此适用相同的规则,块外的代码不能访问块内声明的变量,但也不能重用标识符。

当匿名函数在创建它的函数之外传递时,会创建一个闭包。来自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; }
    } 

实际上,错误似乎与匿名委托或lamda表达式无关。如果您尝试编译以下程序......

using System;

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

        int i = 1;
    }
}

...无论你是否评论,你都会得到完全相同的错误。 错误帮助显示了一个非常相似的案例。我认为不允许这两种情况是合理的,理由是程序员可能会混淆这两个变量。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top