为什么根据我初始化值的方式,将IEnumerable <T>的值转换为不同的行为?

RSi*_*Sid 6 c# casting

我有一些代码,它采用一个值(类型为object)并尝试将其转换为IEnumerable的int和uint,如下所示:

var idList = value as IEnumerable<int>;
var uintList = value as IEnumerable<uint>;
Run Code Online (Sandbox Code Playgroud)

以这种方式初始化值时:

uint number = 1;
object value = new []{ number };
Run Code Online (Sandbox Code Playgroud)

idList和uintList都有值,但调用idList.ToList()会产生一个ArrayTypeMismatchException.但是,当生成值时new List<uint>{ number },idList按预期为null.

此外,var idList = value as IEnumerable<int>;即使使用集合初始值设定项生成值,在VS 2015中调用即时窗口也会返回null,就像我预期的那样.

.Net小提琴在这里重现错误.

这里发生了什么?

Evk*_*Evk 3

int我认为这是因为 C# 和 CLR 处理和之间的转换的方式存在异常差异uint,如本答案中所述。首先请注意,此代码无法编译:

uint[] a1 = new[] { 1u };
var a2 = (int[])a1;
Run Code Online (Sandbox Code Playgroud)

因为 C# 不相信存在强制转换。但是,如果你这样做:

uint[] a1 = new[] { 1u };
var a2 = (int[]) (object) a1;
Run Code Online (Sandbox Code Playgroud)

运行时将决定此转换是否有效,并且它(CLR)以不同的方式思考并允许从uint[]到转换int[](反之亦然),如我链接的答案中所述。

List<int>但对于和则不然List<uint>- CLR 不会以特殊方式处理它们,因此不能在彼此之间进行转换。

因此,在您的情况下,uint[]可以转换为int[]int[]实现IEnumerable<int>,因此您的idList不为空。对于列表来说情况并非如此 - 因此是你的问题。

至于为什么 ToList 在第一种情况下失败,那是因为它在内部做了类似的事情:

 uint[] a1 = new[] { 1u };
 var a2 = (int[]) (object) a1;
 // ToList copies contents to new array
 int[] copy = new int[a2.Length];
 Array.Copy(a2, copy, a2.Length);
Run Code Online (Sandbox Code Playgroud)

Array.Copy直接检查一个数组中的元素类型是否与另一个数组中的元素类型兼容。