If one tries to make a generic struct with the [StructLayout(LayoutKind.Explicit)]
attribute, using the struct generates an exception at runtime:
System.TypeLoadException: Could not load type 'foo' from assembly 'bar' because generic types cannot have explicit layout.
I've been having a hard time finding any evidence that this restriction even exists. The Type.IsExplicitLayout
docs strongly imply that it is allowed and supported. Does anyone know why this isn't allowed? I can't think of any reason why generic types would make it less verifiable. It strikes me as an edge case that they simply didn't bother to implement.
Here's an example of why explicit generic layout would be useful:
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;
}
}
usage:
TaggedUnion<int, double> foo = 1;
Debug.Assert(foo.GetUnionType() == typeof(int));
foo = 1.0;
Debug.Assert(foo.GetUnionType() == typeof(double));
double bar = (double) foo;
Edit:
To be clear, note that layouts aren't verified at compile time even if the struct isn't generic. Reference overlap and x64 differences are detected at runtime by the CLR: http://pastebin.com/4RZ6dZ3S I'm asking why generics are restricted when the checks are done at runtime either way.
It's specified in ECMA 335 (CLI), partition II, section II.10.1.2:
explicit: The layout of the fields is explicitly provided (§II.10.7). However, a generic type shall not have explicit layout.
You can imagine how it could be awkward - given that the size of a type parameter depends on the type parameter, you could get some decidedly odd effects... a reference field isn't allowed to overlap with a built-in value type or another reference, for example, which would be hard to guarantee as soon as unknown sizes are involved. (I haven't looked into how it works out for 32-bit vs 64-bit references, which have a similar but slightly different issue...)
I suspect the specification could have been written to make some more detailed restrictions - but making it a simple blanket restriction on all generic types is considerably simpler.
See more on this question at Stackoverflow