DBN*_*DBN 9 .net c# generics clr unsafe
如果尝试使用该属性创建泛型结构,则使用该结构会在运行时生成异常:[StructLayout(LayoutKind.Explicit)]
System.TypeLoadException:无法从程序集"bar"加载类型'foo',因为泛型类型不能具有显式布局.
我一直很难找到任何证据证明这种限制甚至存在.该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;
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
TaggedUnion<int, double> foo = 1;
Debug.Assert(foo.GetUnionType() == typeof(int));
foo = 1.0;
Debug.Assert(foo.GetUnionType() == typeof(double));
double bar = (double) foo;
Run Code Online (Sandbox Code Playgroud)
编辑:
需要注意的是,请注意,即使结构不是通用的,也不会在编译时验证布局.CLR在运行时检测到引用重叠和x64差异:http://pastebin.com/4RZ6dZ3S 我问为什么在运行时以任何方式完成检查时泛型都受到限制.
它在ECMA 335(CLI),分区II,第II.10.1.2节中指定:
explicit:显式提供字段的布局(§II.10.7).但是,泛型类型不应具有明确的布局.
您可以想象它是如何尴尬的 - 假设类型参数的大小取决于类型参数,您可能会得到一些明显奇怪的效果......参考字段不允许与内置值类型重叠或例如,另一个参考,一旦涉及未知尺寸就很难保证.(我没有研究过32位与64位引用的工作原理,它们有类似但略有不同的问题......)
我怀疑可能已经编写了规范以进行更详细的限制 - 但是对所有泛型类型进行简单的全局限制要简单得多.
问题的根源在于通用性和可验证性,以及基于类型约束的设计。我们不能将引用(指针)与值类型重叠的规则是一个隐式的多参数约束。所以,我们知道 CLR 足够聪明,可以在非泛型情况下验证这一点……为什么不是泛型?听起来很有吸引力。
正确的泛型类型定义是可验证的,适用于今天(在约束范围内)存在的任何类型以及将来定义的任何类型。[1] CLR via C#, Richter编译器自行验证开放泛型类型定义,考虑您指定的任何类型约束以缩小可能的类型参数。
在没有更具体的类型约束的情况下, for Foo<T,U>、 T 和 U 每个都表示所有可能的值和引用类型的联合,以及interface所有这些类型的共同点(基System.Object)。如果我们想让 T 或 U 更具体,我们可以添加主要和次要类型约束。在最新版本的 C# 中,我们可以约束的最具体的是类或接口。不支持结构或原始类型约束。
我们目前不能说:
struct或value type前任:
public struct TaggedUnion<T1, T2>
where T1 : SealedThing // illegal
Run Code Online (Sandbox Code Playgroud)
所以我们没有定义一个通用的类型,它是可验证永远不会违背对所有类型中的重叠规则的方式T和U。即使我们可以通过 struct 进行约束,您仍然可以派生一个带有引用字段的结构,这样对于将来的某些类型,T<,>将是不正确的。
所以我们在这里真正要问的是why don't generic types allow implicit type constraints based on code within the class?;显式布局是一个内部实现细节,它对T1和 的哪些组合T2是合法的施加了限制。在我看来,这与依赖于类型约束的设计不一致。它违反了设计的通用类型系统的干净契约。那么,如果我们打算打破它,为什么还要首先在设计中强加类型约束系统呢?我们不妨把它扔掉,用例外来代替。
从目前的情况来看:
Foo<T,U>对开放定义执行F<,>一次泛型类型的验证。对于 的每个绑定类型实例Foo<t1,u1>,根据约束检查 t1 和 u1 的类型正确性。无需重新验证 的类和方法的代码Foo<t1,u1>。这一切都是“据我所知”
为什么不能从语义上分析每个泛型类型实例化的正确性(C++ 就是证明),没有硬性技术原因,但它似乎破坏了设计。
TL; 博士
在不打破或补充现有类型约束设计的情况下,这是无法验证的。
也许,结合适当的新类型约束,我们可能会在未来看到它。