我收到了此代码段的异常System.ArrayTypeMismatchException: Source array type cannot be assigned to destination array type
:
var uints = GetArray();
if (uints is int[])
{
var list = ((int[])uints).ToList(); // fails when call ToList()
}
private Array GetArray()
{
var result = new uint[] { uint.MaxValue, 2, 3, 4, 5 };
return result;
}
然后我在Why does "int[] is uint[] == true" in C#中求助于Jon的回答,它告诉我由于GetArray()
返回Array
,转换在运行时被推迟,而CLR允许这样做那种int[] to uint[](vice versa)
演员。如果我在转换后检查值,它实际上工作正常:
foreach (var i in ((int[])units))
{
System.Console.WriteLine(i.GetType());
System.Console.WriteLine(i);
}
我会得到:
System.Int32
-1
System.Int32
2
//skipped...
然而,我有点困惑为什么在调用ToList()
时会失败,因为下面这段代码会正常工作?
internal class Animal
{
}
internal class Cat : Animal
{
}
var cats = new Cat[] { new Cat(), new Cat() };
List<Animal> animals = ((Animal[])cats).ToList(); //no exception
答案 0 :(得分:8)
您正在尝试将数组转换为$db['develop'] = array(
'dsn' => '',
'hostname' => 'xxxxxxxxxxx.us-east-1.rds.amazonaws.com',
'username' => 'xxxxxxxx',
'password' => 'xxxxxxxx',
'database' => 'xxxxxxx',
'dbdriver' => 'mysqli',
'dbprefix' => '',
'pconnect' => FALSE,
'db_debug' => (ENVIRONMENT !== 'production'),
'cache_on' => FALSE,
'cachedir' => '',
// 'char_set' => 'utf8',
// 'dbcollat' => 'utf8_general_ci',
'char_set' => 'utf8mb4',
'dbcollat' => 'utf8mb4_unicode_ci',
'swap_pre' => '',
'encrypt' => FALSE,
'compress' => FALSE,
'stricton' => FALSE,
'failover' => array(),
'save_queries' => TRUE
int[]
。这本身很好,因为uint[]
和int[]
都是uint[]
类型,并且将Array
类型的Array对象强制转换为uint[]
(或者int[]
允许(由于某种原因)。
当您尝试将其转换为List时会出现问题。该方法尝试创建类型为int
的List,因为它是您指定的类型,但这不是数组实际的类型。
答案 1 :(得分:4)
这里发生了什么?让我们从foreach
循环开始,C#编译器会对它们进行大量优化,尤其是当您将它们与数组一起使用时 - 它们基本上是对正常for(int i = 0; i < length; i++) { }
的更改 - 所以
foreach (var i in ((int[])units))
{
System.Console.WriteLine(i.GetType());
System.Console.WriteLine(i);
}
无法信任,BTW foreach
也可以对元素类型执行强制转换,最好尝试通用Array.GetValue
方法:
int[] x = ((int[])uints);
Console.WriteLine(x.GetValue(0).GetType()); // System.UInt32
Console.WriteLine(x[0].GetType()); // System.Int32
因此,即使访问x[0]
也可以返回已投放的值,但Array.GetValue
已返回已存在uint
的内容。
让我们做另一个实验:
Console.WriteLine(x.GetType()); // System.UInt32[]
Console.WriteLine(uints.GetType()); // System.UInt32[]
Console.WriteLine(Object.ReferenceEquals(x, uints)); // True
这向我们保证,var x = (int[])uints
中的演员阵容是NOP - 没有操作,它什么都不做。特别是第3行向我们展示了我们得到完全相同的实例。
现在List constructor里面有行
_items = new T[count];
c.CopyTo(_items, 0);
实际上会抛出数组不匹配异常。
但是为什么这个例外不会被抛出,例如当调用GetEnumerator()
时我不了解自己,我预计异常会被抛到行上
x.GetEnumerator()
因为类型IEnumerable<int>
和IEnumerable<uint>
不相容但没有 - 可能是因为.NET返回System.Array+SZArrayEnumerator
,对于每个值类型数组都是相同的。
编辑:(以猫为例)
C#中的数组协方差确保我们可以将任何引用类型数组分配给object[]
,并将Subclass[]
类型的数组分配给BaseClass[]
。值类型的情况不同,因为它们可以具有不同的大小和/或转化行为(uint
与int
)。
ToList
使用内部Array.Copy
调用,当我们查看CRL中的Array.Copy
实现时:https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/classlibnative/bcltype/arraynative.cpp#L328我们看到只有在值类型具有兼容的singess时才能复制该数组由另一个util函数https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/vm/invokeutil.h#L196
另一个问题是为什么以这种方式实施......