类型 T 的表达式不能由类型 X 的模式处理

Expression of type T cannot be handled by a pattern of type X

我已将我的项目升级到目标 C# 7,并使用 Visual Studio 2017 RC 在我的解决方案中实现模式匹配。这样做之后,引入了一些与使用通用参数进行模式匹配有关的错误。

考虑以下代码:

public class Packet
{
}

public class KeepalivePacket : Packet
{
}

public void Send<T>(T packet)
    where T : Packet
{
    if (packet is KeepalivePacket keepalive)
    {
        // Do stuff with keepalive
    }

    switch (packet)
    {
        case KeepalivePacket keepalivePacket:
            // Do stuff with keepalivePacket
            break;
    }
}

if 语句和 case 语句都会产生编译错误。

An expression of type T cannot be handled by a pattern of type KeepalivePacket

如果我首先将参数转换为 object 类型,模式匹配将按预期工作。然后 Roslyn 将转换为 object 标记为多余。

if ((object)packet is KeepalivePacket keepalive)
{
    // This works
}

此错误似乎仅适用于通用参数和变量。 Roslyn 似乎没有意识到这个问题,因为它建议更改代码以通过分析器使用模式匹配,并允许我应用 "code fix" 导致代码损坏。

来自 Microsoft 的 explained by Neal Gafter

The reason it doesn’t work is that there is no conversion (explicit or implicit) defined from T to KeepalivePacket. Pattern matching requires such a conversion to exist, as it is defined in terms of the cast operator, which requires a conversion exist. The language specification and compiler agree that no conversion exists. It seems strange to me that the language specification is defined such that no (explicit) conversion exists here. We'll look at what we can do about that.

We're not going to do anything about this in C# 7. You'll have to add a cast to your code to work around it. Once we have recursive patterns, this may be more difficult to work around. Moreover, the awkward language rule that underlies this issue (i.e. that there is no conversion from T to KeepalivePacket) doesn't make a lot of sense.

更新

现在可以在 C# 7.1 中使用

C# 7.1 现在支持这个。例如,有关配置 LangVersion.

的详细信息,请参阅 this article. You may need to add <LangVersion>7.1</LangVersion> or <LangVersion>latest</LangVersion> to your project file. See here 中的 "Pattern Matching with Generics"

C#7.0 的答案是

if ((Packet)packet is KeepalivePacket keepalive)
{
    // Do stuff with keepalive
}

switch ((Packet)packet)
{
    case KeepalivePacket keepalivePacket:
        // Do stuff with keepalivePacket
        break;
}