为什么可空的模式匹配会导致语法错误?

Why does pattern matching on a nullable result in syntax errors?

我喜欢在 nullable int 上使用 pattern-matchingint?:

int t  = 42;
object tobj = t;    
if (tobj is int? i)
{
    System.Console.WriteLine($"It is a nullable int of value {i}");
}

但是,这会导致以下语法错误:

'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}"".");
}

也按预期工作。

(我用的是 and tested with both and

我认为它与运算符优先级有关。因此,我尝试在几个地方使用括号,但这没有帮助。

为什么会出现这些语法错误,我该如何避免这些错误?

将您的代码更改为:

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? itobj is Nullable<int> i不被允许。

各种形式的类型模式:x is T ycase 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? it is Nullable<int> i 作为模式没有意义:tint,在这种情况下 t is int i 无论如何都会匹配,或者是 null,在这种情况下,任何类型模式都无法匹配。

这就是为什么 t is int? it 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;
}