我今天遇到了这个问题,但不了解发生了什么:
enum Foo
{
Zero,
One,
Two
}
void Main()
{
IEnumerable<Foo> a = new Foo[]{ Foo.Zero, Foo.One, Foo.Two};
IEnumerable<Foo> b = a.ToList();
PrintGeneric(a.Cast<int>());
PrintGeneric(b.Cast<int>());
Print(a.Cast<int>());
Print(b.Cast<int>());
}
public static void PrintGeneric<T>(IEnumerable<T> values){
foreach(T value in values){
Console.WriteLine(value);
}
}
public static void Print(IEnumerable values){
foreach(object value in values){
Console.WriteLine(value);
}
}
输出:
0
1
2
0
1
2
Zero
One
Two
0
1
2
我知道Cast()会导致延迟执行,但仅当实际实现的集合是数组时,它看起来像将其强制转换为IEnumerable结果才会丢失。
为什么Print
方法中的值枚举会导致将enum
其强制转换int
为List<Foo>
集合的,而不是Foo[]
?
这是由于优化,不幸的是,在遇到意外的CLR转换时,优化略有中断。
在CLR级别上,存在从到的引用转换-您实际上根本不需要转换每个对象。在C#级别上并非如此,但在CLR级别上却如此。Foo[]
int[]
现在,Cast<>
包含一个优化说:“如果我已经在处理正确类型的集合,那么我可以只返回相同的引用”-实际上是这样的:
if (source is IEnumerable<T>)
{
return source;
}
因此a.Cast<int>
返回a
,这是一个Foo[]
。当您将其传递给时PrintGeneric
,这很好,因为T
在foreach
循环中存在对的隐式转换。编译器知道IEnumerator<T>.Current
is的类型T
,因此相关的堆栈插槽是type T
。按类型参数JIT编译的代码在将值视为int
而不是时将“做正确的事” Foo
。
但是,当您将数组作为传递时IEnumerable
,的Current
属性IEnumerator
只是类型object
,因此每个值都将被装箱并传递给Console.WriteLine(object)
-,而装箱的对象将为类型Foo
,而不是int
。
这是一些示例代码,显示了其中的第一部分-相信您可以通过剩下的一些简单的代码来理解它:
using System;
using System.Linq;
enum Foo { }
class Test
{
static void Main()
{
Foo[] x = new Foo[10];
// False because the C# compiler is cocky, and "optimizes" it out
Console.WriteLine(x is int[]);
// True because when we put a blindfold in front of the compiler,
// the evaluation is left to the CLR
Console.WriteLine(((object) x) is int[]);
// Foo[] and True because Cast returns the same reference back
Console.WriteLine(x.Cast<int>().GetType());
Console.WriteLine(ReferenceEquals(x, x.Cast<int>()));
}
}
如果尝试在两者之间穿梭uint[]
,您会看到相同的东西int[]
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句