1

为什么 C# 不支持 alpha 转换?

int n = 3;
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
Console.Out.WriteLine("N value = " + n);

屈服:

不能在此范围内声明名为“n”的局部变量,因为它会给“n”赋予不同的含义,后者已在“父或当前”范围中用于表示其他内容

是否有任何我不知道的具体原因,因为它听起来很傻?

4

4 回答 4

11

首先,您对奇数的测试是错误的。

你的问题的答案是否认你的问题的前提。这与 alpha 转换无关。

它也与“词法作用域”无关,leppie 似乎意味着与我对词法作用域的理解不同的东西。C# 是一种词法范围的语言。

现在,我想强调的是,在 C# 中声明两个局部变量,一个隐藏另一个局部变量是非法的。隐藏在其他范围内是完全合法的;类型参数可能会隐藏外部类型参数(尽管这样做真的很愚蠢;不要那样做。)字段可能会隐藏基类字段(尽管您应该将隐藏字段标记为“新”,以强调这一事实.) 本地人可能会隐藏一个方法。等等。

但是一个本地人可能不会隐藏另一个本地人,因为(1)这样做会产生错误农场,并且(2)它违反了关于使用简单名称的更一般的规则。

关于名称的规则是这里有趣的规则。如果你这样做,你会得到一个类似的错误:

class C
{
    int n;
    void M()
    {
        Console.WriteLine(n); // n means this.n
        Func<double, double> f = n=>n; // n means the formal parameter
    }
}

您遇到的错误是因为您违反了 C# 规则,即简单名称必须在首次使用的本地范围内具有一致的含义。

'n' 在一行表示一件事而在下一行表示完全不同的程序是令人困惑和容易出错的,因此是非法的。

如果你想这样做,那么“n”的两个含义必须在不重叠的范围内:

class C
{
    int n;
    void M()
    {
        {
          Console.WriteLine(n); // n means this.n
        }
        Func<double, double> f = n=>n; // n means the formal parameter
    }
}

这将是合法的,因为现在 n 的两种用法都在不重叠的范围内。

该问题与 alpha 转换无关。C# 在需要时可以很好地进行 alpha 转换。

正是因为C# 是词法范围的,所以编译器可以确定您违反了此规则。这并不是 C# 缺乏词法作用域的证据;这是它具有词法作用域的证据。

有关此规则的更多想法,请参阅我关于该主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2009/11/02/simple-names-are-not-so-simple.aspx

于 2012-01-25T14:51:46.897 回答
1

这不是真正的 alpha 转换。

问题是 C# 没有适当的词法作用域,这意味着变量可能在内部作用域中被隐藏。

于 2012-01-25T10:43:38.427 回答
0

恰当的术语是“遮蔽”,如“C# 不允许一个局部变量遮蔽另一个变量”。

限制没有技术原因。阴影不会导致类型检查或代码生成问题。

我相信这种限制的动机纯粹是符合人体工程学的。我希望 Sun 的人在设计 Java 时(注意:Java 也有这个限制,MS 可能会说 eh 听起来很合理,并且也将它放在 C# 中)他们认为阴影会绊倒他们想从C 和 C++ 社区。他们可能认为 Lispers、Smalltalkers 和 MLers 之类的人要么接受它,要么无论如何拒绝使用这种低级的行人语言。

我有点同意他们对人体工程学问题的评估。当您修改一大块代码时,如果您添加新的本地绑定,则可能会无意中捕获变量引用。换句话说,文本程序编辑没有正确的词法范围。

于 2012-01-26T03:40:56.377 回答
0

发生这种情况的原因是在 C# lamda 函数中没有在作用域中声明。他们能够访问父范围内的变量。例如,以下代码允许您的 lambda 访问变量 n:

int outerN = 3; 
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 }; 
int oddNumbers = numbers.Count(localN => localN % 2 == outerN); 
Console.Out.WriteLine("N value = " + outerN); 

您应该将 lambda 函数内的变量视为与声明它们的方法在同一范围内。

来自MSDN

Lambda 可以引用在其中定义 lambda 的封闭方法或类型范围内的外部变量。以这种方式捕获的变量将被存储以在 lambda 表达式中使用,即使变量会超出范围并被垃圾回收。在 lambda 表达式中使用外部变量之前,必须明确分配它。

以下规则适用于 lambda 表达式中的变量范围:

  • 在引用它的委托超出范围之前,不会对捕获的变量进行垃圾收集。

  • 在 lambda 表达式中引入的变量在外部方法中不可见。

  • lambda 表达式不能直接从封闭方法中捕获 ref 或 out 参数。

  • lambda 表达式中的 return 语句不会导致封闭方法返回。

  • lambda 表达式不能包含 goto 语句、break 语句或 continue 语句,其目标位于主体之外或包含的匿名函数的主体中。

于 2012-01-25T10:47:03.480 回答