空传播运算符、out 参数和错误的编译器错误?
Null propagation operator, out parameters and false compiler errors?
假设我有一个 class,它有一个 Dictionary<string,string>
类型的 属性,它可能为 null。
这可以编译,但对 TryGetValue()
的调用可能会在运行时引发 NullRef
异常:
MyClass c = ...;
string val;
if(c.PossiblyNullDictionary.TryGetValue("someKey", out val)) {
Console.WriteLine(val);
}
所以我添加了一个 null 传播运算符来防止出现 null,但这无法编译:
MyClass c = ...;
string val;
if( c.PossiblyNullDictionary ?. TryGetValue("someKey", out val) ?? false ) {
Console.WriteLine(val); // use of unassigned local variable
}
是否存在 val
将在 if
块内未初始化的实际用例,或者编译器是否可以简单地不推断这一点(以及为什么)?
更新:解决此问题的最干净(?)方法^H^H^H^H^H 是:
MyClass c = ...;
string val = null; //POW! initialized.
if( c.PossiblyNullDictionary ?. TryGetValue("someKey", out val) ?? false ) {
Console.WriteLine(val); // no more compiler error
}
那是因为如果 c.PossiblyNullDictionary 为 null,则不会执行 TryGetValue 并且该表达式不会 return true 或 false。
c.PossiblyNullDictionary ?. TryGetValue("someKey", out val) returns 可空,你可以用这样的代码替换你的代码并编译:
string val;
var result = c.PossiblyNullDictionary?.TryGetValue("key", out val);
if (result.HasValue && result.Value)
{
}
似乎您 运行 限制了编译器对 ?.
和 ??
的理解,这并不奇怪,因为它们并没有真正完全融入语言。
如果您在没有较新运算符的情况下显式测试,编译器会同意您的看法:
MyClass c = new MyClass();
string val;
if (c.PossiblyNullDictionary != null && c.PossiblyNullDictionary.TryGetValue("someKey", out val)) {
Console.WriteLine(val); // now okay
}
通过将 val
初始化为一个错误值(例如 String.Empty
),编译器能够理解空运算符的意图并按预期运行(通过 LINQPad,natch):
void Main()
{
MyClass c = new MyClass();
string val = string.Empty;
if (c.PossiblyNullDictionary?.TryGetValue("someKey", out val) ?? false)
{
Console.WriteLine(val);
}
}
public class MyClass {
public Dictionary<string, string> PossiblyNullDictionary;
}
// Define other methods and classes here
Ed:来自 'grok the intent' 我的意思是,如果编译器允许执行离开当前范围且 val
未初始化,则编译器无法对程序的特性做出重要保证。当它评估空运算符时,方法调用。
您要求的用例是这样的:
假设我们有 bool SomeMethod(string s, out v)
而不是 TryGetValue。假设在调用时,SomeMethod
是调皮的,只是有一个 return true;
的主体。编译器将方法调用体视为不透明的(因为它可能并不总是在编译器的程序集 available/visible 中),因此它得出结论,无法证明 val
曾经被初始化。
编辑:
作为对一些评论的回应,我想更新我的回答以指出此行为并非特定于 ??
或 ?.
C# 语言功能;您可以简单地使用三元表达式重现相同的效果:
c.PossiblyNullDictionary == null ?
false :
c.PossiblyNullDictionary.TryGetValue("someKey", out val)
//error: use of possibly uninitialized local variable
假设我有一个 class,它有一个 Dictionary<string,string>
类型的 属性,它可能为 null。
这可以编译,但对 TryGetValue()
的调用可能会在运行时引发 NullRef
异常:
MyClass c = ...;
string val;
if(c.PossiblyNullDictionary.TryGetValue("someKey", out val)) {
Console.WriteLine(val);
}
所以我添加了一个 null 传播运算符来防止出现 null,但这无法编译:
MyClass c = ...;
string val;
if( c.PossiblyNullDictionary ?. TryGetValue("someKey", out val) ?? false ) {
Console.WriteLine(val); // use of unassigned local variable
}
是否存在 val
将在 if
块内未初始化的实际用例,或者编译器是否可以简单地不推断这一点(以及为什么)?
更新:解决此问题的最干净(?)方法^H^H^H^H^H 是:
MyClass c = ...;
string val = null; //POW! initialized.
if( c.PossiblyNullDictionary ?. TryGetValue("someKey", out val) ?? false ) {
Console.WriteLine(val); // no more compiler error
}
那是因为如果 c.PossiblyNullDictionary 为 null,则不会执行 TryGetValue 并且该表达式不会 return true 或 false。
c.PossiblyNullDictionary ?. TryGetValue("someKey", out val) returns 可空,你可以用这样的代码替换你的代码并编译:
string val;
var result = c.PossiblyNullDictionary?.TryGetValue("key", out val);
if (result.HasValue && result.Value)
{
}
似乎您 运行 限制了编译器对 ?.
和 ??
的理解,这并不奇怪,因为它们并没有真正完全融入语言。
如果您在没有较新运算符的情况下显式测试,编译器会同意您的看法:
MyClass c = new MyClass();
string val;
if (c.PossiblyNullDictionary != null && c.PossiblyNullDictionary.TryGetValue("someKey", out val)) {
Console.WriteLine(val); // now okay
}
通过将 val
初始化为一个错误值(例如 String.Empty
),编译器能够理解空运算符的意图并按预期运行(通过 LINQPad,natch):
void Main()
{
MyClass c = new MyClass();
string val = string.Empty;
if (c.PossiblyNullDictionary?.TryGetValue("someKey", out val) ?? false)
{
Console.WriteLine(val);
}
}
public class MyClass {
public Dictionary<string, string> PossiblyNullDictionary;
}
// Define other methods and classes here
Ed:来自 'grok the intent' 我的意思是,如果编译器允许执行离开当前范围且 val
未初始化,则编译器无法对程序的特性做出重要保证。当它评估空运算符时,方法调用。
您要求的用例是这样的:
假设我们有 bool SomeMethod(string s, out v)
而不是 TryGetValue。假设在调用时,SomeMethod
是调皮的,只是有一个 return true;
的主体。编译器将方法调用体视为不透明的(因为它可能并不总是在编译器的程序集 available/visible 中),因此它得出结论,无法证明 val
曾经被初始化。
编辑:
作为对一些评论的回应,我想更新我的回答以指出此行为并非特定于 ??
或 ?.
C# 语言功能;您可以简单地使用三元表达式重现相同的效果:
c.PossiblyNullDictionary == null ?
false :
c.PossiblyNullDictionary.TryGetValue("someKey", out val)
//error: use of possibly uninitialized local variable