【发布时间】:2019-07-26 22:49:51
【问题描述】:
考虑:
enum Foo
{
Bar,
Quux,
}
void Main()
{
var enumValues = new[] { Foo.Bar, Foo.Quux, };
Console.WriteLine(enumValues.GetType()); // output: Foo[]
Console.WriteLine(enumValues.First().GetType()); // output: Foo
var intValues = enumValues.Cast<int>();
Console.WriteLine(intValues.GetType()); // output: Foo[] ???
Console.WriteLine(intValues.First().GetType()); // output: Int32
Console.WriteLine(ReferenceEquals(enumValues, intValues)); // true
var intValuesArray = intValues.ToArray();
Console.WriteLine(intValuesArray.GetType()); // output: Int32[]
Console.WriteLine(intValuesArray.First().GetType()); // output: Int32
Console.WriteLine(ReferenceEquals(intValues, intValuesArray)); // false
}
注意第三个Console.WriteLine - 我希望它打印数组被转换为的类型(Int32[]),但它打印的是原始类型(Foo[])!并且ReferenceEquals 确认确实,第一个Cast<int> 调用实际上是空操作。
于是我偷看了source of Enumerable.Cast,发现如下:
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source)
{
IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
if (typedSource != null) return typedSource;
if (source == null) throw Error.ArgumentNull("source");
return CastIterator<TResult>(source);
}
就我们的意图和目的而言,唯一重要的是前两行,因为它们是唯一被调用的行。这意味着该行:
var intValues = enumValues.Cast<int>();
被有效地翻译成:
var intValues = ((IEnumerable)enumValues) as IEnumerable<int>;
但是,删除非泛型 IEnumerable 的强制转换会导致编译器错误:
var intValues = enumValues as IEnumerable<int>; // error
我一直在摸不着头脑,为什么会这样,我认为这与 Array 实现了非泛型 IEnumerable 并且所有C# 中数组的各种特殊大小写,但老实说我不确定。请有人向我解释这里发生了什么以及为什么?
【问题讨论】:
-
好吧,ReSharper 甚至抱怨
((IEnumerable)enumValues) as IEnumerable<int>永远是null,这显然是错误的。 -
你所说的特殊类型是协方差,
IEnumerable<out t>。实际上是不允许这样做的:var intValues = enumValues as IEnumerable<int>;这是众所周知的。它打印 Foo[] 而不是int的原因是int默认情况下是枚举的基础类型...