我似乎偶然发现了C#中不寻常的一些我不完全理解的东西.假设我定义了以下枚举:
public enum Foo : short
{
// The values aren't really important
A,
B
}
Run Code Online (Sandbox Code Playgroud)
如果我声明一个数组Foo或检索一个Enum.GetValues,我能够成功地将它转换为IEnumerable<short>和IEnumerable<ushort>.例如:
Foo[] values = Enum.GetValues( typeof( Foo ) );
// This cast succeeds as expected.
var asShorts = (IEnumerable<short>) values;
// This cast also succeeds, which wasn't expected.
var asUShorts = (IEnumerable<ushort>) values;
// This cast fails as expected:
var asInts = (IEnumerable<int>) values;
Run Code Online (Sandbox Code Playgroud)
对于其他基础类型也会发生这种情况.枚举数组似乎总是可以转换为底层整数类型的有符号和无符号版本.
我无法解释这种行为.这是明确定义的行为还是我偶然发现了语言或CLR的特殊怪癖?
Jon*_*eet 10
它不仅仅是IEnumerable<T>- 您可以将其强制转换为数组类型,只要您先欺骗编译器:
public enum Foo : short
{
A, B
}
class Test
{
static void Main()
{
Foo[] foo = new Foo[10];
short[] shorts = (short[]) (object) foo;
ushort[] ushorts = (ushort[]) (object) foo;
}
}
Run Code Online (Sandbox Code Playgroud)
C#语言并不期望这种转换是可行的,但CLR很乐意这样做.反过来也是如此 - 你可以从a转换short[]为a Foo[].哦,如果你有另一个具有相同基础类型的枚举,你也可以投射到那个.基本上,CLR知道所有这些类型都只是16位整数 - 所有位模式都是有效值,即使它们具有不同的含义 - 因此它允许您将一种类型视为另一种类型.我不认为这是在CLI规范中记录的 - 无论如何我找不到任何引用.
当LINQ(in Cast<>和ToList)中的优化组合在一起时,这可能会导致一些有趣的问题,仅用于记录.
例如:
int[] foo = new int[10];
var list = foo.Cast<uint>().ToList();
Run Code Online (Sandbox Code Playgroud)
您可能会想到这会产生一个InvalidCastException(例如,如果您从List<int>带有任何值的a开始),而是最终得到:
Unhandled Exception: System.ArrayTypeMismatchException: Source array type cannot be assigned to destination array type.
at System.Array.Copy(Array sourceArray, Int32 sourceIndex, Array destinationArray, Int32 destinationIndex, Int32 length, Boolean reliable)
at System.SZArrayHelper.CopyTo[T](T[] array, Int32 index)
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Test.Main()
Run Code Online (Sandbox Code Playgroud)
这是因为Cast操作已经检查并发现它int[]已经是a uint[],所以它一直传递给它ToList(),然后尝试使用Array.Copy,这确实注意到了差异.哎哟!