为啥 C# 允许将泛型数组与所有类型的模式进行匹配?

Posted

技术标签:

【中文标题】为啥 C# 允许将泛型数组与所有类型的模式进行匹配?【英文标题】:Why does C# allow matching a generic array against patterns of all types?为什么 C# 允许将泛型数组与所有类型的模式进行匹配? 【发布时间】:2021-12-27 16:10:30 【问题描述】:

我有一个泛型类型T 的数组,我想检查长度是否为0。但是,我打错了,而不是Array.Length is 0,我输入了Array is 0。当我注意到这个错字时,我想知道为什么它没有给我一个编译时错误,当我使用任何具体类型的数组(如stringintobject,甚至dynamic。显然,无论数组的底层类型是什么,数组都不能是int 值为零,所以这是值得怀疑的。我尝试了各种其他 int 文字、字符串文字,我什至尝试了小于和大于模式,所有这些都奏效了。我尝试更改数组的维度或可空性(因为我的原始示例是 T[,]?),我什至尝试向 T 泛型类型添加一些约束,但没有任何改变。

是否存在一些特殊情况,这些模式实际上可以匹配(即使我无法想象,因为它们仍然只是数组),还是编译器错误?如果是后者,是什么原因造成的?


由于有人要求提供代码,因此这是一个简单的测试,它给了我相同的结果: (假设 T 是任何泛型类型)

T[] array = new T[42];
// You can assign anything here
// Only thing that matters is that the variable is a T[] with any number of dimensions
if (array is 0 or "test") Console.WriteLine("This shouldn't even compile")

【问题讨论】:

您没有收到编译时错误,因为is 进行了运行时检查。见docs.microsoft.com/en-us/dotnet/csharp/language-reference/… 理论上编译器可以给你一个错误,因为给定public static bool Test<T>(T[] a)然后a is 0永远没有意义。 @MatthewWatson 当然可能给我一个错误,它应该,但它确实'不。那是我的问题。 @VicF 但它确实给我任何非泛型数组的错误。在那里有所作为甚至没有意义。数组就是数组。 @Nyde 这就是我的意思——它可以,但它没有。 Resharper 警告您:"The source expression never matches the provided pattern". 【参考方案1】:

我认为这是 C# 7.1 中以下specification change 的结果:

现有 C# as operator 的规范允许存在 操作数的类型和指定的类型之间没有转换 当任何一个都是开放类型时。但是,在 C# 7 中,类型标识符 模式要求输入的类型之间存在转换 和给定的类型。

我们建议放宽这一点,将表达式更改为类型标识符,在 除了在被允许的条件下被允许 C# 7,当表达式作为类型被允许时也被允许。 具体来说,新案例是表达式的类型 或者指定的类型是开放类型。

改变自己:

左侧的静态类型和 给定类型被认为不兼容并导致编译时 错误。静态类型 E 的值被称为与模式兼容 类型 T 如果存在身份转换,则为隐式 引用转换,装箱转换,显式引用 转换,或从 E 到 T 的拆箱转换,或者如果是 E 或 T 是开放型。如果 E 类型的表达式是编译时错误 与它所在的类型模式中的类型不兼容 匹配。

由于此更改,如果 is 的左侧是开放的泛型类型(您的问题就是这种情况) - 那么它被认为是他们所说的与右侧的“模式兼容”。

但是,特定的泛型类型如string[] 违反了规范中提到的其他限制,因此与 int 不“模式兼容”,因此会导致编译错误。

T[] array = new T[10];
bool test = array is int; // that's fine with this proposal, since left sid e is open type

现在关于您正在使用的 constant pattern 的文档 (is 0) 说:

当输入值不是开放类型时,常量表达式为 隐式转换为匹配表达式的类型;如果 输入值的类型与 常量表达式,模式匹配操作出错。

正如我们所知道的,开放类型(T[],其中 T 未解析)泛型数组与 int 模式兼容,因此编译器可以满足您的表达式,而 string[](或任何其他封闭的泛型数组) 与模式不兼容并导致错误。

您可以在第一个链接中查看动机部分,了解他们决定以这种方式更改规范的原因。

【讨论】:

但这有什么意义呢?我的意思是,很明显(并且应该对编译器而言)数组,无论底层类型是什么,都不能是 intstring 或其他任何东西。那么为什么要引入这个呢? 这不是为数组引入的,而是为了解决泛型的其他模式匹配问题。他们决定让任何开放的泛型类型模式与其他任何东西兼容,这可能是最简单的方法。也许他们忽略了数组,或者这没什么大不了的。您可以在他们的 github 上创建一个问题,以从第一手获取此信息。 你能把它链接到我吗? 泛型问题示例:github.com/dotnet/roslyn/issues/16195。您可以在该存储库中创建问题。 好吧,最后一件事:在示例问题中,他们提供了 C# 编译器的确切版本。如何查看我拥有的特定版本?我只能找到 Visual Studio 和 .NET Framework 的版本。

以上是关于为啥 C# 允许将泛型数组与所有类型的模式进行匹配?的主要内容,如果未能解决你的问题,请参考以下文章

将泛型 T 的类型解析为 C# 中的特定接口

Java 将泛型类型与 Void 进行比较

为啥我不能将泛型类型的一个实例转换为另一个实例?

将泛型与可能是值或引用类型的 null 进行比较?

再谈怎样以最简单的方法将泛型为String类型的集合或String类型的数组转化为逗号间隔字符串形式

可以将泛型类型限制为枚举吗? [复制]