为什么可空的模式匹配会导致语法错误?
Why does pattern matching on a nullable result in syntax errors?
我喜欢在 nullable int
上使用 pattern-matching
即 int?
:
int t = 42;
object tobj = t;
if (tobj is int? i)
{
System.Console.WriteLine($"It is a nullable int of value {i}");
}
但是,这会导致以下语法错误:
- CS1003: Syntax error, ';',
- CS1525: Invalid expression term ')',
- CS0103: The name 'i' does not exist in the current context.
'i)' 用红色波浪线标记。
表达式在使用旧运算符时编译 is
:
int t = 42;
object tobj = t;
if (tobj is int?)
{
System.Console.WriteLine($"It is a nullable int");
}
string t = "fourty two";
object tobj = t;
if (tobj is string s)
{
System.Console.WriteLine($@"It is a string of value ""{s}"".");
}
也按预期工作。
(我用的是c#-7.2 and tested with both .net-4.7.1 and .net-4.6.1)
我认为它与运算符优先级有关。因此,我尝试在几个地方使用括号,但这没有帮助。
为什么会出现这些语法错误,我该如何避免这些错误?
将您的代码更改为:
int t = 42;
object tobj = t;
if (tobj is Nullable<int> i)
{
Console.WriteLine($"It is a nullable int of value {i}");
}
这会产生更多帮助:
- CS8116:在模式中使用可空类型 'int?' 是不合法的;使用基础类型 'int' 代替 (找不到有关 CS8116 的文档以供参考)
其他(Microsoft 用户 @Blue0500 at github ) have tagged this behaviour as a bug Roslyn issue #20156. Reacting to Roslyn issue #20156, Julien Couvreur 表示他认为这是设计使然。
Neal Gafter from Microsoft working on Roslyn has also said better diagnostics are wanted for use of nullable type is switch pattern.
因此,可以使用以下方法避免错误消息:
int t = 42;
object tobj = t;
if (tobj == null)
{
Console.WriteLine($"It is null");
}
else if (tobj is int i)
{
Console.WriteLine($"It is a int of value {i}");
}
除了issues when parsingtobj is int? i
,还有一个问题,为什么tobj is int? i
或tobj is Nullable<int> i
不被允许。
各种形式的类型模式:x is T y
、case T y
等,always fails to match when x
is null
. This is because null
doesn't have a type,所以问"is this null
of this type?"是一个毫无意义的问题。
因此 t is int? i
或 t is Nullable<int> i
作为模式没有意义:t
是 int
,在这种情况下 t is int i
无论如何都会匹配,或者是 null
,在这种情况下,任何类型模式都无法匹配。
这就是为什么 t is int? i
或 t is Nullable<int> i
不被编译器支持,并且可能永远不会被编译器支持的原因。
使用 t is int? i
时编译器会出现额外错误的原因是,例如t is int? "it's an int" : "no int here"
是有效的语法,因此编译器会对您在此上下文中对可空类型使用 ?
的尝试感到困惑。
至于如何避免它们,显而易见的(虽然可能不是很有帮助)答案是:不要使用可空类型作为类型模式中的类型。一个更有用的答案将要求您解释为什么您正在尝试这样做。
对于任何想知道如何实际使用可空值模式匹配的人,您可以使用通用辅助函数来实现,如下所示:
public static bool TryConvert<T>(object input, out T output)
{
if (input is T result)
{
output = result;
return true;
}
output = default(T);
// Check if input is null and T is a nullable type.
return input == null && System.Nullable.GetUnderlyingType(typeof(T)) != null;
}
这将 return true
如果 T
是与 input
包含的相同类型的可为空或不可为空的,或者如果 input
为空T
可以为空。基本上与正常工作相同,但也处理可空值。
旁注:有趣的是,根据我的测试,我发现如果 T 可为空,System.Nullable.GetUnderlyingType(typeof(T))
每次调用时都会分配 40 个字节的垃圾。不知道为什么,对我来说似乎是一个错误,但这可能是要付出高昂的代价,而不是像平常那样进行空值检查。
知道了,这里有一个更好的函数:
public static bool TryConvert<T>(object input, out T? output) where T : struct
{
if (input is T result)
{
output = result;
return true;
}
output = default(T?);
return input == null;
}
我喜欢在 nullable int
上使用 pattern-matching
即 int?
:
int t = 42;
object tobj = t;
if (tobj is int? i)
{
System.Console.WriteLine($"It is a nullable int of value {i}");
}
但是,这会导致以下语法错误:
- CS1003: Syntax error, ';',
- CS1525: Invalid expression term ')',
- CS0103: The name 'i' does not exist in the current context.
'i)' 用红色波浪线标记。
表达式在使用旧运算符时编译 is
:
int t = 42;
object tobj = t;
if (tobj is int?)
{
System.Console.WriteLine($"It is a nullable int");
}
string t = "fourty two";
object tobj = t;
if (tobj is string s)
{
System.Console.WriteLine($@"It is a string of value ""{s}"".");
}
也按预期工作。
(我用的是c#-7.2 and tested with both .net-4.7.1 and .net-4.6.1)
我认为它与运算符优先级有关。因此,我尝试在几个地方使用括号,但这没有帮助。
为什么会出现这些语法错误,我该如何避免这些错误?
将您的代码更改为:
int t = 42;
object tobj = t;
if (tobj is Nullable<int> i)
{
Console.WriteLine($"It is a nullable int of value {i}");
}
这会产生更多帮助:
- CS8116:在模式中使用可空类型 'int?' 是不合法的;使用基础类型 'int' 代替 (找不到有关 CS8116 的文档以供参考)
其他(Microsoft 用户 @Blue0500 at github ) have tagged this behaviour as a bug Roslyn issue #20156. Reacting to Roslyn issue #20156, Julien Couvreur 表示他认为这是设计使然。
Neal Gafter from Microsoft working on Roslyn has also said better diagnostics are wanted for use of nullable type is switch pattern.
因此,可以使用以下方法避免错误消息:
int t = 42;
object tobj = t;
if (tobj == null)
{
Console.WriteLine($"It is null");
}
else if (tobj is int i)
{
Console.WriteLine($"It is a int of value {i}");
}
除了issues when parsingtobj is int? i
,还有一个问题,为什么tobj is int? i
或tobj is Nullable<int> i
不被允许。
各种形式的类型模式:x is T y
、case T y
等,always fails to match when x
is null
. This is because null
doesn't have a type,所以问"is this null
of this type?"是一个毫无意义的问题。
因此 t is int? i
或 t is Nullable<int> i
作为模式没有意义:t
是 int
,在这种情况下 t is int i
无论如何都会匹配,或者是 null
,在这种情况下,任何类型模式都无法匹配。
这就是为什么 t is int? i
或 t is Nullable<int> i
不被编译器支持,并且可能永远不会被编译器支持的原因。
使用 t is int? i
时编译器会出现额外错误的原因是,例如t is int? "it's an int" : "no int here"
是有效的语法,因此编译器会对您在此上下文中对可空类型使用 ?
的尝试感到困惑。
至于如何避免它们,显而易见的(虽然可能不是很有帮助)答案是:不要使用可空类型作为类型模式中的类型。一个更有用的答案将要求您解释为什么您正在尝试这样做。
对于任何想知道如何实际使用可空值模式匹配的人,您可以使用通用辅助函数来实现,如下所示:
public static bool TryConvert<T>(object input, out T output)
{
if (input is T result)
{
output = result;
return true;
}
output = default(T);
// Check if input is null and T is a nullable type.
return input == null && System.Nullable.GetUnderlyingType(typeof(T)) != null;
}
这将 return true
如果 T
是与 input
包含的相同类型的可为空或不可为空的,或者如果 input
为空T
可以为空。基本上与正常工作相同,但也处理可空值。
旁注:有趣的是,根据我的测试,我发现如果 T 可为空,System.Nullable.GetUnderlyingType(typeof(T))
每次调用时都会分配 40 个字节的垃圾。不知道为什么,对我来说似乎是一个错误,但这可能是要付出高昂的代价,而不是像平常那样进行空值检查。
知道了,这里有一个更好的函数:
public static bool TryConvert<T>(object input, out T? output) where T : struct
{
if (input is T result)
{
output = result;
return true;
}
output = default(T?);
return input == null;
}