gpt4 book ai didi

c# - 为什么转换为 IEnumerable 的数组会忽略延迟执行?

转载 作者:可可西里 更新时间:2023-11-01 08:23:22 26 4
gpt4 key购买 nike

我今天遇到了这个问题,但我不明白发生了什么:

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 级别,有一个来自 Foo[]引用 转换至 int[] - 你实际上根本不需要转换每个物体。在 C# 级别不是这样,但在 CLR 级别是这样。

现在,Cast<>包含一个优化说“如果我已经在处理正确类型的集合,我可以只返回相同的引用”——实际上是这样的:

if (source is IEnumerable<T>)
{
return source;
}

所以 a.Cast<int>返回 a ,这是一个 Foo[] .将它传递给 PrintGeneric 就可以了,因为这样就会隐式转换为 Tforeach环形。编译器知道 IEnumerator<T>.Current 的类型是T , 所以相关栈槽的类型是 T .当将值视为 int 时,每个类型参数的 JIT 编译代码将“做正确的事”而不是 Foo .

但是,当您将数组作为 IEnumerable 传递时, Current IEnumerator 上的属性(property)只是类型 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[]顺便说一下。

关于c# - 为什么转换为 IEnumerable 的数组会忽略延迟执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19772153/

26 4 0