为啥 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
。当我注意到这个错字时,我想知道为什么它没有给我一个编译时错误,当我使用任何具体类型的数组(如string
、int
、object
,甚至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[]
(或任何其他封闭的泛型数组) 与模式不兼容并导致错误。
您可以在第一个链接中查看动机部分,了解他们决定以这种方式更改规范的原因。
【讨论】:
但这有什么意义呢?我的意思是,很明显(并且应该对编译器而言)数组,无论底层类型是什么,都不能是int
或 string
或其他任何东西。那么为什么要引入这个呢?
这不是为数组引入的,而是为了解决泛型的其他模式匹配问题。他们决定让任何开放的泛型类型模式与其他任何东西兼容,这可能是最简单的方法。也许他们忽略了数组,或者这没什么大不了的。您可以在他们的 github 上创建一个问题,以从第一手获取此信息。
你能把它链接到我吗?
泛型问题示例:github.com/dotnet/roslyn/issues/16195。您可以在该存储库中创建问题。
好吧,最后一件事:在示例问题中,他们提供了 C# 编译器的确切版本。如何查看我拥有的特定版本?我只能找到 Visual Studio 和 .NET Framework 的版本。以上是关于为啥 C# 允许将泛型数组与所有类型的模式进行匹配?的主要内容,如果未能解决你的问题,请参考以下文章