39

C#の静的コンストラクターに関するドキュメントには次のように書かれています。

静的コンストラクターは、静的データを初期化するため、または1回だけ実行する必要がある特定のアクションを実行するために使用されます。最初のインスタンスが作成される前、または静的メンバーが参照される前に、自動的に呼び出されます。

その最後の部分(それが自動的に呼び出されるときについて)は私をループに投げ込みました。その部分を読むまでは、何らかの方法でクラスにアクセスするだけで、その基本クラスの静的コンストラクターが呼び出されたことを確認できると思いましたドキュメントをテストおよび調査したところ、これは当てはまらないことが明らかになりました。基本クラスの静的コンストラクターは、その基本クラスのメンバーが具体的にアクセスされるまで実行されることが保証されていないようです。

さて、ほとんどの場合、派生クラスを扱っているときは、インスタンスを作成し、これが作成中の基本クラスのインスタンスを構成するため、静的コンストラクターが呼び出されると思います。しかし、派生クラスの静的メンバーのみを扱っている場合はどうなりますか?

これをもう少し具体的にするために、私は以下のコードが機能すると思いました:

abstract class TypeBase
{
    static TypeBase()
    {
        Type<int>.Name = "int";
        Type<long>.Name = "long";
        Type<double>.Name = "double";
    }
}

class Type<T> : TypeBase
{
    public static string Name { get; internal set; }
}

class Program
{
    Console.WriteLine(Type<int>.Name);
}

クラスにアクセスすると、;Type<T>の静的コンストラクターが自動的に呼び出されると思いました。TypeBaseしかし、そうではないようです。Type<int>.Namenull、であり、上記のコードは空の文字列を出力します。

ダミーメンバー(Initialize()何もしない静的メソッドなど)を作成する以外に、派生型のいずれかが使用される前に、基本型の静的コンストラクターが呼び出されるようにするためのより良い方法はありますか?

そうでなければ...ダミーメンバーです!

4

6 に答える 6

28

静的コンストラクターを明示的に呼び出すことができるため、初期化のためのメソッドを作成する必要はありません。

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof (TypeBase).TypeHandle);

派生クラスの静的コンストラクターで呼び出すことができます。

于 2011-01-11T00:03:53.497 に答える
20

他の人が指摘しているように、あなたの分析は正しいです。仕様はここで文字通り実装されています。基本クラスのメンバーが呼び出されておらず、インスタンスも作成されていないため、基本クラスの静的コンストラクターは呼び出されません。それがいかに驚くべきことかはわかりますが、それは仕様の厳密で正しい実装です。

「それをするときに痛いなら、それをしないでください」以外のアドバイスはありません。反対のケースもあなたを噛む可能性があることを指摘したかっただけです:

class Program 
{
  static void Main(string[] args)
  {      
    D.M();
  }      

}
class B 
{ 
  static B() { Console.WriteLine("B"); }
  public static void M() {}
} 
class D: B 
{ 
  static D() { Console.WriteLine("D"); }
}

これは、「Dのメンバー」が呼び出されたにもかかわらず、「B」を出力します。Mは継承のみによってDのメンバーです。CLRには、BMが「Dを介して」呼び出されたか「Bを介して」呼び出されたかを区別する方法がありません。

于 2011-01-11T00:33:22.450 に答える
14

ここでのルールは非常に複雑であり、CLR2.0とCLR4.0の間では、実際には微妙で興味深い方法で変更されており、IMOはCLRバージョン間でほとんどの「巧妙な」アプローチを脆弱にします。Initialize()メソッドがフィールドに接触しない場合、CLR4.0ではメソッドが機能しない可能性もあります。

私は別のデザインを探すか、おそらくあなたのタイプで通常の怠惰な初期化を使用します(つまり、ビットまたは参照を(に対してnull)チェックして、それが行われたかどうかを確認します)。

于 2011-01-10T23:15:25.100 に答える
3

すべてのテストで、ベースのダミーメンバーを呼び出して、ベースが静的コンストラクターを呼び出すようにすることしかできませんでした。

class Base
{
    static Base()
    {
        Console.WriteLine("Base static constructor called.");
    }

    internal static void Initialize() { }
}

class Derived : Base
{
    static Derived()
    {
        Initialize(); //Removing this will cause the Base static constructor not to be executed.
        Console.WriteLine("Derived static constructor called.");
    }

    public static void DoStaticStuff()
    {
        Console.WriteLine("Doing static stuff.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Derived.DoStaticStuff();
    }
}

もう1つのオプションは、次のことを行う派生型に静的読み取り専用メンバーを含めることでした。

private static readonly Base myBase = new Base();

ただし、これは、ベースの静的コンストラクターを呼び出すためだけのハックのように感じます(ダミーメンバーもそうですが)。

于 2011-01-10T23:13:06.910 に答える
3

私はほとんどいつもこのようなものに頼っていることを後悔しています。静的メソッドとクラスは、後で制限する可能性があります。後でTypeクラスの特別な動作をコーディングしたい場合は、ボックスに入れられます。

だからここにあなたのアプローチのわずかなバリエーションがあります。これはもう少しコードですが、後でカスタムタイプを定義して、カスタム処理を実行できるようになります。

    abstract class TypeBase
    {
        private static bool _initialized;

        protected static void Initialize()
        {
            if (!_initialized)
            {
                Type<int>.Instance = new Type<int> {Name = "int"};
                Type<long>.Instance = new Type<long> {Name = "long"};
                Type<double>.Instance = new Type<double> {Name = "double"};
                _initialized = true;
            }
        }
    }

    class Type<T> : TypeBase
    {
        private static Type<T> _instance;

        public static Type<T> Instance
        {
            get
            {
                Initialize();
                return _instance;
            }
            internal set { _instance = value; }
        }

        public string Name { get; internal set; }
    }

その後、仮想メソッドをTypeに追加し、Typeの特別な実装が必要になったときに、次のように実装できます。

class TypeInt : Type<int>
{
    public override string Foo()
    {
        return "Int Fooooo";
    }
}

そして、変更して接続します

protected static void Initialize()
{
      if (!_initialized)
      {
          Type<int>.Instance = new TypeInt {Name = "int"};
          Type<long>.Instance = new Type<long> {Name = "long"};
          Type<double>.Instance = new Type<double> {Name = "double"};
          _initialized = true;
       }
}

私のアドバイスは、静的コンストラクターを避けることです-それは簡単です。また、静的クラスと、可能な場合は静的メンバーを避けてください。私は決して控えめに言っているのではありません。静的よりもクラスのシングルトンを優先します。

于 2011-01-11T02:50:20.463 に答える
0

ただのアイデア、あなたはこのようなことをすることができます:

    abstract class TypeBase
    {
        static TypeBase()
        {
            Type<int>.Name = "int";
            Type<long>.Name = "long";
            Type<double>.Name = "double";
        }
    }

    class Type<T> : TypeBase
    {
        static Type() 
        {
            new Type<object>();
        }

        public static string Name { get; internal set; }
    }

    class Program
    {
        Console.WriteLine(Type<int>.Name);
    }
于 2011-01-11T00:03:39.333 に答える