6

コード:

class Base<T,U> where T:Base<T,U>,new()  where U :class
{
    protected static U _val = null;
    internal static void ShowValue()
    {
        if(_val == null)new T(); //Without this line, it won't work as expected
        Console.WriteLine (_val);
    }
    internal static void Virtual()
    {
        Console.WriteLine ("Base");
    }
}
class Deriv :Base<Deriv,string>
{
    static Deriv()
    {
        _val = "some string value";
    }
    internal static new void Virtual ()
    {
        Console.WriteLine ("Deriv");
    }
}
 public static void Main (string[] args)
{
    Deriv.ShowValue();            
    Deriv.Virtual();
}

.NET のジェネリックのおかげで、ジェネリック基本クラスで定義されたジェネリック静的メソッドを再利用して、一連の特定のクラスを作成できます。継承ポリモーフィズムをある程度模倣できます。しかし、異なるバージョンの静的フィールドを初期化するには、静的コンストラクターを使用する必要があります。残念ながら、それらを直接呼び出すことはできないため、その呼び出しをトリガーする方法を見つけ出す必要があります。上記の例は方法を示しています。しかし、インスタンス化やリフレクションのアプローチは好きではありません。また、ジェネリック パラメーターの静的メソッドに制約を作成することもできません。それで、この種の仕事をする別の方法があれば、私は尋ねたいです!

よろしくお願いします!

~~~~~~~~~~~~~~~~

いくつかの結論(少し早いかもしれません):

この種の状況に対処するための回避策はないようです。サブクラスをインスタンス化するか、リフレクションを使用する必要があります。.cctors を一度だけ呼び出す必要があることを考えると、パラメーターなしの ctor をユーザーに公開しないように、 new() 制約を選択できない場合があるため、リフレクション アプローチを支持します。

さらに実験を行った後、.cctors は複数回呼び出される可能性がありますが、最初の呼び出しのみが静的フィールドの設定に影響することがわかりました。それは奇妙ですが、良い奇妙さです!

    class MyClass 
    {
        static int _val = 0;
        static MyClass()
        {
            _val++;
            Console.WriteLine (_val);
        }
    }
    public static void Main (string[] args)
    {
        ConstructorInfo ci = typeof(MyClass).TypeInitializer;
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
        ci.Invoke(new object[0]);
    }
//result:
//1
//1
//1
//1
4

5 に答える 5

6

デザインを再考することを強くお勧めします。この種の回避策を「静的継承」に使用しようとすると、.NET のコア設計のいくつかに反することになります。

あなたが解決しようとしているより大きな問題は不明ですが、継承をシミュレートするためにこのような「賢い」コードを試みると、長期的には保守と診断が非常に難しいコードになります。

のメンバーを実際に使用する(またはそのインスタンスを作成する) ことなくDeriv、基本的に静的コンストラクターをトリガーしません。Deriv.ShowValue()基本的に への呼び出しに変換されることを理解することが重要です

Base<Deriv, string>.ShowValue();

...だから、実際には何も呼び出していませんDeriv。そのように記述されている場合、呼び出しコードは実際にはより明確になります。

編集:型初期化子を明示的に使用しないようにするもう 1 つの (明らかに残念な) 理由は、.NET 4.5 にバグがあり、場合によっては例外が不適切にスローされることです。詳細については、トピックに関する私の質問を参照してください。

于 2011-08-02T06:30:50.553 に答える
3

正しい解決策は、次のように型初期化子 (= 静的コンストラクター) を呼び出すことです。

typeof(T).TypeInitializer.Invoke(null, null);

両方が必要nullです。1 つだけ指定するとMemberAccessException.

したがって、コードは次のようになります。

internal static void ShowValue()
{
    if (_val == null)
    {
        if (typeof(T).TypeInitializer != null)
            typeof(T).TypeInitializer.Invoke(null, null);
        if (_val == null)
            throw new InvalidOperationException(string.Format("The type initializer of {0} did not initialize the _val field.", typeof(T)));
    }
    Console.WriteLine(_val);
}

これで、new()制約を削除できます。

于 2012-11-16T14:39:04.920 に答える
2

静的コンストラクターがいつ実行されるかを制御することはできませんが、静的プロパティまたはメソッドにアクセスする前、およびインスタンス化の前に実行されることが保証されています。

静的コンストラクターを以前の時点で実行する必要はありません。クラスから何も使用していないが、静的コンストラクターのコードを実行したい場合は、設計に問題があります。

于 2011-08-02T06:21:19.790 に答える
0

静的コンストラクターに関する MSDN の記事と、ページの約 10% を参照してください。

最初のインスタンスが作成される前、または静的メンバーが参照される前に、クラスを初期化するために静的コンストラクターが自動的に呼び出されます。

于 2011-08-02T06:22:29.893 に答える
0

静的コンストラクターは自動的に一度だけです。自分で呼び出すことはできません。

ここからの例:

public class Bus
{
    // Static constructor:
    static Bus()
    {
        System.Console.WriteLine("The static constructor invoked.");
    }    

    public static void Drive()
    {
        System.Console.WriteLine("The Drive method invoked.");
    }
}

class TestBus
{
    static void Main()
    {
        Bus.Drive();
    }
}

出力:

The static constructor invoked.

The Drive method invoked.
于 2011-08-02T06:19:06.180 に答える