你的两个例子虽然不同,但都是无效的。
您不能将一种对象类型的数组转换为另一种对象类型,即使它们之间存在转换运算符(显式或隐式)。编译器正确地阻止了这种强制转换。该规则的例外是是否存在继承关系;由于数组协方差,您可以向下转换为基本类型(对于引用类型)。以下作品:
class A {}
class B : A {}
B[] bs = new[] { new B() };
A[] result = (A[])bs; // valid
见SharpLab
同样的原则也适用于 LINQ 中的 Cast<T> 方法——除非类型匹配,否则将在运行时枚举时抛出异常。下面的答案是不正确的。例如,您不能将 Cast 一个数组 double 到 int。当然,如果不枚举结果(如示例中),则不会发生异常。然而,在实际枚举 (foreach, ToList, ToArray) 时,将抛出 InvalidCastException。
var array = new double[2];
array[0] = 10;
array[1] = 20;
var temp = array.Cast<int>(); // OK, not enumerated
var converted = temp.ToList(); // bam! InvalidCastException
请注意temp 变量——因为在下面的答案中,由于 LINQ 的延迟执行,它不会抛出。一旦你枚举它,它就会失败。见SharpLab。
Cast 方法旨在弥补与前泛型集合的差距,其中值在内部存储为object 的数组,而集合本身仅实现IEnumerable。 Cast 允许转换为 IEnumerable<T>,但是除了从 object 到原始类型之外,不允许转换/转换。
对于结构,这是显而易见的——一个装箱的double 只能拆箱为double;不能将其拆箱为int。以简单的非数组情况为例:
double d = 1.5;
object o = d;
int iOk = (int)(double)o; // ok
int iBad = (int)o; // fails
见SharpLab
那么,Cast<int> 将失败是有道理的,因为该方法仅将单个转换插入到 int,而不将中间转换插入到 double,否则将需要。
对于类,Cast 将只插入直接转换。该方法是通用的,不/不能考虑任何用户定义的运算符。因此,当您说“有两个可以相互转换的类”时,这仍然无关紧要。换句话说,以下将失败:
class A {}
class B {
public static implicit operator A(B b) => new A();
}
B[] bs = new[] { new B() };
var temp = bs.Cast<A>(); // OK, not yet enumerated
A[] result = temp.ToArray(); // throws InvalidCastException
见SharpLab
同样(如上所述),该规则的例外是两个类之间是否存在继承关系。你可以从一个到另一个:
class A {}
class B : A {}
B[] bs = new[] { new B() };
A[] result = bs.Cast<A>().ToArray(); // valid
见SharpLab
另一种方法是使用 LINQ 的 Select 投影您的原始集合,应用您想要的转换运算符:
class A {}
class B {
public static implicit operator A(B b) => new A();
}
B[] bs = new[] { new B() };
A[] result = bs.Select(b => (A)b).ToArray(); // valid!
见SharpLab。这也适用于double/int:
var array = new double[] { 10.2, 20.4 };
int[] result = array.Select(d => (int)d).ToArray();
见SharpLab