【问题标题】:Static constructor called twice静态构造函数调用了两次
【发布时间】:2017-03-19 23:35:23
【问题描述】:

以前我有一个 static TopUp() 方法,我想用 Baseclass 的静态构造函数替换它,因为它“只执行一次”,如 msdn 所述。
msdn: Static Constuctor
将派生类的初始化保留在构造函数中并只执行一次的任何解决方案?

class BaseClass<T>
{
    static BaseClass()
    {
        for (byte i = 0; i < 2; i++)
        {
            var temp = new Junction<byte>[2] { new Level2<byte>(), new OtherLevel2<byte>() };
            Program.myList.Add(temp);
        }
        Console.WriteLine("static BaseClass()");
    }
}

abstract class Junction<T> : BaseClass<T> { }

sealed class Level2<T> : Junction<T> { }

sealed class OtherLevel2<T> : Junction<T> { }

class Program
{
    internal static List<Junction<byte>[]> myList = new List<Junction<byte>[]>();
    static Program()
    {
        BaseClass<object> callTheStaticMethod = new BaseClass<object>();
    }

    static void Main()
    {
        Console.WriteLine("myList.Count = " + myList.Count);
        Console.ReadLine();
    }
}

Output:  
static BaseClass()  
static BaseClass()  
myList.Count = 4

【问题讨论】:

    标签: c# arrays list constructor static


    【解决方案1】:

    你的静态构造只被调用一次。每种类型一次,即。

    每次您将BaseClass&lt;T&gt; 与不同的类型参数一起使用时,这是一种完全不同的类型。因此,继承BaseClass&lt;byte&gt;Junction&lt;byte&gt; 是与BaseClass&lt;object&gt; 不同的类型。调用BaseClass&lt;byte&gt; 的静态构造函数,以及BaseClass&lt;object&gt; 的静态构造函数。

    从您的问题中并不清楚您实际上想要实现什么。我会说,在静态构造函数中使用Junction&lt;byte&gt; 强烈表明您的类根本不是真正的通用类。 BaseClass&lt;T&gt; 的任何其他使用必然仍然依赖于Junction&lt;byte&gt;,因此也依赖于BaseClass&lt;byte&gt;。这可能会否定您认为通过使类通用化可以获得的任何好处。

    您可以通过将静态构造函数移动到由BaseClass&lt;T&gt; 继承的非泛型基类来强制静态构造函数只执行一次。例如:

    class BaseClass { /* static constructor here */ }
    class BaseClass<T> : BaseClass { /* other stuff */ }
    

    但鉴于课程一开始就缺乏通用性,目前尚不清楚这是否真的有很大帮助。这里似乎有一个更广泛的设计缺陷需要解决。

    【讨论】:

    • 我清除了所有不必要的代码,这就是为什么看起来通用的原因。只是学习并尝试使用泛型。我没有想到两个不同的参数会创建两种不同的类型,但现在看起来很明显。谢谢你。它有效。
    【解决方案2】:

    问题是您在 typed 基类中有静态初始化程序。问题是BaseClass&lt;string&gt;BaseClass&lt;int&gt; 被认为是两种不同的类型。实际的类是在编译时生成的,因此编译器会为每个变体复制静态初始化器。

    如果您更改静态初始化程序中的最后一行以包含类型的名称,您将能够更好地了解这个问题。

    在 .Net 4.6 中,您可以这样做:

    Console.WriteLine($"static BaseClass<{nameof(T)}>()");
    

    在 .Net 4.0 或更高版本中,您可以这样做:

    string typeName = typeof(T).FullName;
    Console.WriteLine(string.Format("static BaseClass<{0}>()", typeName));
    

    要解决您的问题,请在没有类型参数的标准类中进行静态初始化。在这种情况下,您可以简单地从 BaseClass 中删除 &lt;T&gt; 类型参数。示例:

    class BaseClass
    {
        static BaseClass()
        {
            for (byte i = 0; i < 2; i++)
            {
                var temp = new Junction<byte>[2] { new Level2<byte>(), new OtherLevel2<byte>() };
                Program.myList.Add(temp);
            }
    
            Console.WriteLine($"static BaseClass<{nameof(T)}>()");
        }
    }
    
    abstract class Junction<T> : BaseClass { }
    
    sealed class Level2<T> : Junction<T> { }
    
    sealed class OtherLevel2<T> : Junction<T> { }
    

    【讨论】:

    • 我认为您的想法是正确的,但是您的术语(“类型化”与“没有类型的类”)是非标准的,并且对于谈论泛型非常不清楚。
    • 我在任何地方都没有看到我指的是没有类型的类。我很难解析您在评论中的意思。
    • 这是您上一段的直接引述。
    • 好的,我现在看到了。清理语言。
    • “最重要的是,每次向泛型提供类型参数时,都会对静态初始化程序进行新的调用”——这是真的。但是,您的解释方式听起来像是在说静态构造函数中的代码(实际上是该类型的所有代码)对于每个不同的T 都是重复的。这是不正确的,当T 是引用类型时。
    猜你喜欢
    • 2012-09-25
    • 1970-01-01
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    • 1970-01-01
    • 2019-05-09
    • 2019-12-02
    • 1970-01-01
    相关资源
    最近更新 更多