我有一些类似于以下的代码.
class MyClass<TEnum> where TEnum : struct
{
public IEnumerable<TEnum> Roles { get; protected set; }
public MyClass()
{
IEnumerable<string> roles = ... ;
TEnum value;
Roles = from r in roles
where Enum.TryParse(r, out value)
select value; // <---- ERROR HERE!
}
}
Run Code Online (Sandbox Code Playgroud)
但是,在上面指出的行上,我收到错误:
使用未分配的局部变量'value'
在我看来,value总是会在这种情况下初始化,因为它是一个out参数Enum.TryParse.
这是C#编译器的错误吗?
不它不是.
编译器无法保证Enum.TryParse(r, out value)将被执行.
如果roles是空集合怎么办?
即使您在方法中初始化您的集合,CSC也不会考虑roles使用值 - 这是编译器当前无法做到的事情.
如果Enum.TryParse(r, out value)不执行lambda会怎么样- value通过闭包不会得到它的值?
编译器不能给你这样的保证.
您的代码(部分)等效于:
class MyClass<TEnum> where TEnum : struct
{
public IEnumerable<TEnum> Roles { get; protected set; }
public MyClass()
{
IEnumerable<string> roles = ... ;
Roles = GetValues(); // <---- ERROR HERE!
}
public static IEnumerable<TEnum> GetValues(IEnumerable<String> roles)
{
TEnum value;
String[] roleArray = roles.ToArray(); // To avoid the foreach loop.
// What if roleArray.Length == 0?
for(int i = 0; i < roleArray.Length; i++)
{
// We will never get here
if (Enum.TryParse(roleArray[i], out value))
yield return value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码对于编译器来说是干净且易懂的(没有错误) - 它知道如果没有执行,Enum.TryParse(roleArray[i], out value)你就不会尝试返回value.
但功能LINQ查询并不是那么简单.
如果我们使用Enumerable扩展重写它,我们将:
TEnum value;
Roles = roles
.Where(role => Enum.TryParse(role, out value))
.Select(role => value); <---- STILL ERROR HERE!
Run Code Online (Sandbox Code Playgroud)
我们再次得到错误.
编译器无法看到value无疑会设置,因为它不了解所用方法的内部 - Where可能或(理论上)可能不会执行lambda,所以如果你添加value变量用于闭包的事实,它就变得非常重要任务是做出这样的保证,没有误报.
TL; DR:错误表示变量(可证明)未分配 - FALSE.现实,变量不能被证明(使用编译器可用的证明定理).
LINQ的设计假设是纯函数......那些根据输入返回输出并且没有副作用的函数.
一旦重写,它将是:
roles.Where(r => Enum.TryParse(r, out value)).Select(r => value);
Run Code Online (Sandbox Code Playgroud)
并重新改写
Enumerable.Select(Enumerable.Where(roles, r => Enum.TryParse(r, out value)), r => value);
Run Code Online (Sandbox Code Playgroud)
这些LINQ函数将在调用选择lambda之前调用过滤器lambda,但编译器无法知道(至少,没有特殊套管或跨模块数据流分析).更有问题的是,如果Where通过重载决策选择了不同的实现,则可能TryParse不会调用lambda .
编译器的明确赋值规则非常简单,而且在安全方面也是错误的.
这是另一个例子:
bool flag = Blah();
int value;
if (flag) value = 5;
return flag? value: -1;
Run Code Online (Sandbox Code Playgroud)
使用未初始化的值是不可能的,但数据流分析的语言规则会导致编译错误,value而不是"明确分配".
但是,编译器错误的措辞很差.不是"明确分配"与肯定是"未分配"不一样,因为错误声称.
| 归档时间: |
|
| 查看次数: |
351 次 |
| 最近记录: |