我发现以下内容相当奇怪。然后,我主要使用动态语言中的闭包,这对于同一个“bug”应该是不可怀疑的。以下内容使编译器不满意:
VoidFunction t = delegate { int i = 0; };
int i = 1;
它说:
名为“i”的局部变量不能 在此范围内声明,因为它 会给'我'一个不同的含义, 已经在“孩子”中使用 范围来表示别的东西
所以这基本上意味着在委托中声明的变量将具有声明的函数的范围。不完全是我所期望的。我甚至没有尝试过调用这个函数。至少Common Lisp有一个功能,你可以说变量应该有一个动态名称,如果你真的希望它是本地的。这在创建不泄漏的宏时尤其重要,但这样的东西在这里也会有所帮助。
所以我想知道其他人如何解决这个问题呢?
澄清我正在寻找一个解决方案,其中我在delegete中声明的变量不会干扰委托后声明的变量。我希望仍然能够捕获在委托之前声明的变量。
答案 0 :(得分:9)
必须允许匿名方法(和lambdas)在包含方法中使用作用域的局部变量和参数。
变通方法是为变量使用不同的名称,或者创建一个普通的方法。
答案 1 :(得分:3)
匿名函数创建的“闭包”与其他动态语言创建的“闭包”略有不同(我将使用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
,if
,等)
因此适用相同的规则,块外的代码不能访问块内声明的变量,但也不能重用标识符。
当匿名函数在创建它的函数之外传递时,会创建一个闭包。来自Javascript示例的变化是只保留匿名函数实际使用的那些变量,因此在这种情况下持有的对象事情完成后,o2将立即用于GC,
答案 2 :(得分:1)
你也可以从这样的代码中获得CS0136:
int i = 0;
if (i == 0) {
int i = 1;
}
“i”的第二个声明的范围是明确的,像C ++这样的语言没有任何优点。但C#语言设计师决定禁止它。鉴于上述片段,您认为仍然认为这是一个坏主意吗?抛出一堆额外的代码,你可以盯着这段代码一段时间而不会看到错误。
解决方法是微不足道的,无痛的,只是想出一个不同的变量名。
答案 3 :(得分:0)
这是因为委托可以引用委托之外的变量:
int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };
答案 4 :(得分:0)
如果我没记错的话,编译器会创建匿名方法中引用的外部变量的类成员,以使其工作。
这是一种解决方法:
class Program
{
void Main()
{
VoidFunction t = RealFunction;
int i = 1;
}
delegate void VoidFunction();
void RealFunction() { int i = 0; }
}
答案 5 :(得分:0)
实际上,错误似乎与匿名委托或lamda表达式无关。如果您尝试编译以下程序......
using System;
class Program
{
static void Main()
{
// Action t = delegate
{
int i = 0;
};
int i = 1;
}
}
...无论你是否评论,你都会得到完全相同的错误。 error help显示了一个非常相似的案例。我认为不允许这两种情况是合理的,理由是程序员可能会混淆这两个变量。