如果尝试使用[StructLayout(LayoutKind.Explicit)]
属性创建通用结构体,在运行时使用该结构体将会引发异常:
System.TypeLoadException: Could not load type 'foo' from assembly 'bar' because generic types cannot have explicit layout.
我一直找不到任何证据证明这种限制的存在。 Type.IsExplicitLayout文档强烈暗示它是允许和支持的。有人知道为什么不允许吗?我想不出泛型类型会使验证变得更加困难的理由。这似乎只是一个他们没有费力去实现的边缘情况。
这里是显示显式泛型布局将是有用的示例:
public struct TaggedUnion<T1,T2>
{
public TaggedUnion(T1 value) { _union=new _Union{Type1=value}; _id=1; }
public TaggedUnion(T2 value) { _union=new _Union{Type2=value}; _id=2; }
public T1 Type1 { get{ if(_id!=1)_TypeError(1); return _union.Type1; } set{ _union.Type1=value; _id=1; } }
public T2 Type2 { get{ if(_id!=2)_TypeError(2); return _union.Type2; } set{ _union.Type2=value; _id=2; } }
public static explicit operator T1(TaggedUnion<T1,T2> value) { return value.Type1; }
public static explicit operator T2(TaggedUnion<T1,T2> value) { return value.Type2; }
public static implicit operator TaggedUnion<T1,T2>(T1 value) { return new TaggedUnion<T1,T2>(value); }
public static implicit operator TaggedUnion<T1,T2>(T2 value) { return new TaggedUnion<T1,T2>(value); }
public byte Tag {get{ return _id; }}
public Type GetUnionType() {switch(_id){ case 1:return typeof(T1); case 2:return typeof(T2); default:return typeof(void); }}
_Union _union;
byte _id;
void _TypeError(byte id) { throw new InvalidCastException(/* todo */); }
[StructLayout(LayoutKind.Explicit)]
struct _Union
{
[FieldOffset(0)] public T1 Type1;
[FieldOffset(0)] public T2 Type2;
}
}
用法:
TaggedUnion<int, double> foo = 1;
Debug.Assert(foo.GetUnionType() == typeof(int));
foo = 1.0;
Debug.Assert(foo.GetUnionType() == typeof(double));
double bar = (double) foo;
编辑:
为了明确,即使结构体不是泛型的,布局也没有在编译时进行验证。CLR会在运行时检测引用重叠和x64差异:http://pastebin.com/4RZ6dZ3S 我想知道为什么泛型会受到限制,当检查也是在运行时进行。
T
本身是具有显式布局的值类型...或者假设有两个类型参数,两个参数都具有显式布局,并且它们在泛型类型的布局中重叠。我认为这变得非常复杂,以至于最好禁止它。我远非这个特定领域的专家,但我知道有很多地方会排除可能的功能,以使全局禁令更简单。 - Jon SkeetFoo<>
的有效性(不考虑T
),并确定T
是否遵守Foo<T>
指定的任何约束来评估类型Foo<T>
的有效性。 - supercat