【问题标题】:Why does pattern matching on a nullable result in syntax errors?为什么可空值的模式匹配会导致语法错误?
【发布时间】:2018-01-19 14:09:46
【问题描述】:

我喜欢在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}");
}

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

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

也可以按预期工作。

(我正在使用 并使用 进行了测试)

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

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

【问题讨论】:

  • 您为什么要尝试使用可为空的类型?您是否尝试使用模式匹配创建可选值,例如 F# 的可区分联合?他们来了,只是不在 C# 8 时间范围内(我认为)。如果您使用公共接口为有效值和缺失值创建两种不同的类型,例如interface IOption<T>{}; class MyValidClass:IOption<T>{...} class MyEmptyType:IOption<T>{},则可以模拟它们。接口和选项甚至不需要有任何方法
  • @PanagiotisKanavos 可空类型来自代码的不同部分,使用可空类型的选择与我在这里尝试的模式匹配无关。

标签: c#-7.2 .net-4.7.1 .net-4.6.1 c# pattern-matching nullable c#-7.0


【解决方案1】:

各种形式的类型模式:x is T ycase T y 等、always fails to match when x is null。这是因为null doesn't have a type,所以问“这是null这种类型吗?”是个毫无意义的问题。

因此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" 是有效的语法,因此编译器会因为您尝试在此上下文中将 ? 用于可空类型而感到困惑。

至于如何避免它们,显而易见(尽管可能不是很有帮助)的答案是:不要使用可空类型作为类型模式中的类型。更有用的答案需要您解释为什么您尝试这样做。

【讨论】:

    【解决方案2】:

    将您的代码更改为:

    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 的文档以供参考)

    其他人(用户@Blue0500 at github)将此行为标记为错误Roslyn issue #20156。对来自 Microsoft 的 Roslyn issue #20156Julien Couvreur 的反应说他认为这是设计使然。
    来自 Microsoft 的 Neal Gafter 在 Roslyn 上工作也说过 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&lt;int&gt; i不允许的问题。

    【讨论】:

    • 不适合用户想做什么来使用if(t.GetType() == typeof(int?))
    • 我猜在你问答的所有样本中,t is 应该是tobj is
    【解决方案3】:

    对于想知道如何实际使用可空值的模式匹配的任何人,您可以使用通用辅助函数来做到这一点,如下所示:

    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;
    }
    

    如果T 是与input 包含的相同类型的可空或不可空,或者input 为空且T 可空,这将返回true。基本上和正常工作一样,但也处理空值。


    旁注:有趣的是,从我的测试中,我发现System.Nullable.GetUnderlyingType(typeof(T)) 每次调用它时都会分配 40 字节的垃圾,如果 T 可以为空。不知道为什么,对我来说似乎是一个错误,但这可能是一个沉重的代价,而不是像往常一样进行空检查。

    知道了,这里有一个更好的函数:

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 2015-08-02
      • 1970-01-01
      • 2017-10-07
      • 1970-01-01
      • 2015-12-11
      • 2012-05-14
      • 2011-04-21
      • 1970-01-01
      • 2010-12-09
      相关资源
      最近更新 更多