为什么泛型结构不能具有在C#中指定泛型类型的静态成员?

时间:2020-09-12 04:30:43

标签: c# generics struct typeloadexception

很抱歉,如果这是重复的!我四处搜寻,但找不到解释。下面的玩具示例在尝试实例化此结构时立即为我提供TypeLoadException。如果我使用一个类,或者相反,不要在静态成员中指定泛型类型(将其保留为T),则效果很好。

public struct Point<T>
{
    static Point<int> IntOrigin = new Point<int>(0, 0);

    T X { get; }
    T Y { get; }

    public Point(T x, T y)
    {
        this.X = x;
        this.Y = y;
    }
}

我比较复杂的实际情况归结为这样的事情,所以我真的很想了解为什么它会发出TypeLoadException。

1 个答案:

答案 0 :(得分:2)

This commentanother comment,在Github上最接近解决当前事务状态,指出为什么不允许这种自引用结构定义,并且很可能不允许在可预见的将来。

即使静态成员在将其包含在类型布局中之前也需要对其进行初始化,但是类型初始化也需要对该静态成员进行初始化。这种初始化依赖关系的循环会创建Catch-22,从而导致运行时异常。

根据this comment ,. NET Core在此模式下可以正常工作。但是,当我在.NET Core项目中尝试您的示例时,发现了相同的失败。因此,要么该注释有误,要么实例成员方案与静态成员方案之间存在细微差别(我不愿意对此进行任何进一步研究)。

有趣的是,dotNETFiddle.net上使用的编译器会发出编译时错误"Struct member 'struct2 field' of type 'struct1' causes a cycle in the struct layout"。我不知道为什么Visual Studio编译器似乎不再产生此错误(已在2017年和2019年检查)。在我看来,好像是另一个错误。但是围绕Github的讨论似乎接受了该代码在技术上是有效的(即,根据C#规范),因此可能在某个时候有意识地决定要删除编译器错误,并让CLR进行投诉。运行时。

请注意,错误参考页中的建议建议更改为class而不是struct。当然,在使用struct的情况下,这通常是不可行的;值类型可能很重要。但是,在您的特定示例中,实际上有一个基于该想法的简单解决方法。由于您的字段不是struct实例的实际布局的一部分,因此您可以将其移到专门用于此类值的静态类中。例如:

public struct Point<T>
{
    public static class Constants
    {
        static Point<int> IntOrigin = new Point<int>(0, 0);
    }

    T X { get; }
    T Y { get; }

    public Point(T x, T y)
    {
        this.X = x;
        this.Y = y;
    }
}

然后,您需要使用Point<double>.IntOrigin而不是Point<double>.Constants.IntOrigin。由于每种类型的类型初始化都可以独立完成,因此初始化周期不会发生,并且代码可以正常运行。