如果尝试使用[StructLayout(LayoutKind.Explicit)]
属性创建通用结构,则使用该结构会在运行时生成异常:
我一直很难找到任何证据表明该限制甚至存在。 Type.IsExplicitLayout
文档强烈暗示它是允许和支持的。有谁知道为什么不允许这样做?我想不出通用类型会使它的可验证性降低的任何原因。令我震惊的是,他们根本没有去实现它们。
这是an example为何明确的通用布局有用的原因:
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
我在问为什么在运行时以任何一种方式进行检查时都限制了泛型。
最佳答案
问题的根源是通用性和可验证性,以及基于类型约束的设计。我们不能将引用(指针)与值类型重叠的规则是隐式的多参数约束。因此,我们知道CLR足够聪明,可以在非通用情况下验证这一点……为什么不通用呢?听起来很吸引人。
正确的泛型类型定义是可验证的,可以用于今天存在的任何类型(在约束范围内)以及将来将要定义的任何类型。 [1]通过C#通过Richter进行CLR编译器会考虑您指定的任何类型约束来缩小可能的类型参数的范围,从而自行验证开放通用类型定义。
在没有更具体的类型约束的情况下,对于Foo<T,U>
,T和U分别表示所有可能的值和引用类型的并集,以及所有这些类型通用的interface
(基础System.Object
)。如果要使T或U更具体,可以添加主类型约束和辅助类型约束。在最新版本的C#中,我们可以限制的最具体的是类或接口(interface)。不支持struct或基本类型约束。
我们目前不能说:
struct
或value type
前任:
public struct TaggedUnion<T1, T2>
where T1 : SealedThing // illegal
因此,我们无法定义可验证的泛型类型,而永远不会违反
T
和U
中所有类型的重叠规则。即使我们可以通过struct进行约束,您仍然可以派生带有引用字段的struct,这样对于将来的某些类型,T<,>
将是不正确的。因此,我们真正要问的是
why don't generic types allow implicit type constraints based on code within the class?
;显式布局是内部实现的详细信息,它对T1
和T2
的合法组合施加了限制。我认为,这与依赖类型约束的设计不一致。它违反了设计的通用类型系统的明确契约(Contract)。那么,如果我们打算破坏类型约束系统,那为什么还要在设计中首先经历它呢?我们不妨扔掉它,并用异常(exception)代替它。以目前的状态来看:
Foo<T,U>
进行一次通用类型F<,>
的验证。对于Foo<t1,u1>
的每个绑定(bind)类型实例,将根据约束检查t1和u1的类型正确性。无需为Foo<t1,u1>
的类和方法重新验证代码。 所有这些都是“据我所知”
没有硬技术上的原因为什么不能对每个泛型实例化进行语义分析以确保其正确性(C++证明了这一点),但这似乎破坏了设计。
TL; DR
如果不破坏或补充现有的类型约束设计,就无法对此进行验证。
也许,结合适当的新类型约束,我们将来可能会看到它。