首页 > 解决方案 > 为什么泛型结构不能具有在 C# 中指定泛型类型的静态成员?

问题描述

抱歉,如果这是重复的!我四处寻找,但找不到解释。当我尝试实例化这个结构时,下面的玩具示例给了我一个 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。

标签: c#genericsstructtypeloadexception

解决方案


Github 上的这条评论另一条评论最接近于解决当前的事态,指出为什么不允许使用这种自引用结构定义,并且在可预见的未来可能不会。

即使是静态成员也需要初始化类型才能包含在类型布局中,但类型初始化需要初始化该静态成员。这个初始化依赖循环创建了导致运行时异常的 Catch-22。

根据这个评论,.NET Core 可以很好地使用这种模式。但是当我在 .NET Core 项目中尝试您的示例时,我发现了同样的失败。因此,要么该评论有误,要么实例成员场景和您的静态成员场景之间存在一些细微差别(除此之外我没有费心进行进一步调查)。

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

请注意,错误参考页面中的建议建议更改为 aclass而不是 a struct。当然,这在使用 a 时通常是不可行的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. 由于每种类型的类型初始化都可以独立完成,因此不会出现初始化中的循环,代码运行良好。


推荐阅读