本文介绍了为什么泛型类型不能有显式布局?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果尝试使用[(.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:http://pastebin.com/4RZ6dZ3S在运行时检测到引用重叠和x64差异我在问为什么当检查在运行时以任何一种方式完成时,泛型都会受到限制。

推荐答案

问题的根源在于通用性和可验证性,以及基于类型约束的设计。不能将引用(指针)与值类型重叠的规则是一个隐式的多参数约束。因此,我们知道CLR足够聪明,可以在非通用情况下验证这一点...为什么不是普通的呢?听起来很吸引人。

正确的泛型类型定义是对于现有的任何类型(在约束内)和将来将定义的任何类型,今天都可以验证为有效的定义。[1]CLR通过C#,Richter编译器会考虑您为缩小可能的类型参数而指定的任何类型约束,自行验证开放泛型类型定义。

在没有更具体的类型约束的情况下,对于Foo<T,U>,T和U分别表示所有可能的值和引用类型的联合,以及所有这些类型的公共interface(基本System.Object)。如果我们想使T或U更具体,我们可以添加主要类型约束和次要类型约束。在最新版本的C#中,我们可以约束的最具体的是一个类或一个接口。不支持结构或基元类型约束。

我们目前不能说:

  1. 其中仅structvalue type
  2. 如果T是密封类型,则为T

例如:

public struct TaggedUnion<T1, T2>
    where T1 : SealedThing   // illegal

因此,我们无法定义一个泛型类型,该泛型类型可以验证为永远不会违反TU中所有类型的重叠规则。即使我们可以通过结构进行约束,您仍然可以派生具有引用字段的结构,以便对于将来的某个类型,T<,>将不正确。

因此,我们在这里真正问的是why don't generic types allow implicit type constraints based on code within the class?;显式布局是一个内部实现细节,它对T1T2的哪些组合是合法的施加了限制。在我看来,这与依赖类型约束的设计不一致。它违反了泛型类型系统设计的干净约定。那么,如果我们打算打破类型约束系统,为什么还要在设计中不厌其烦地施加类型约束系统呢?我们不妨扔掉它,用例外取而代之。

当前状态:

  1. 类型约束是开放泛型类型的可视元数据
  2. 泛型类型Foo<T,U>的验证在开放定义F<,>上执行一次。对于Foo<t1,u1>的每个绑定类型实例,根据约束检查T1和U1的类型正确性。不需要反转Foo<t1,u1>的类和方法的代码。

所有这些都是"据我所知"

每个泛型类型实例化都不能对其正确性进行语义分析(C++就是证明),这在技术上是没有道理的,但它似乎破坏了设计。

TL;DR

如果不打破或补充现有的类型约束设计,这是无法验证的。

也许,结合适当的新类型约束,我们将来可能会看到它。

这篇关于为什么泛型类型不能有显式布局?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-02 14:31