8

これに関する質問、または静的継承の同様のトピックを読むときはいつでも、これは通常サポートされていないという回答があり(私たちはそれを知っています)、これは貧弱な設計であり、おそらくより良い方法があるためです。やれ。私はそれを行うためのより良い方法を見つけたいので、すべての提案を受け入れます-これが私がやろうとしていることです。

インスタンスデータを持たないクラスがあります。すべてのメソッドは静的です。これを呼びましょうclass BaseStatic。この静的クラスから継承し、いくつかの新しい静的メソッドを追加する新しい静的クラス(もちろんいくつかありますが、1つに固執することができます)が必要です。これを呼び出しましょうSubStatic

消費者に書いてもらいたいのは:

SubStatic.MethodFromSub();

そしてまた

SubStatic.MethodFromBase();

私も書くことができることを知っています:

BaseStatic.MethodFromBase()

明示的にですが、消費者はどのクラスがどのメソッドを実装するかを知る必要があります。ある静的クラスを別の静的クラスから継承できないため、継承ではこれを行うことができません。それで、それを行うためのより良い方法は何ですか?

これで、これらのクラスをインスタンスクラスとして持つことができ、すべてのメソッドを静的として定義できることがわかりました。これにより、上記の動作が得られますが、他の問題が発生します。

  1. これを行うと、メソッドが親の静的クラスで実行されているためSubStatic.MethodFromBase()SubStatic静的コンストラクターは呼び出されません(親の静的コンストラクターが呼び出されます)

  2. 静的親メソッドの1つが、サブクラスがオーバーライドできる別のメソッドを呼び出す必要がある場合は、サブクラスに仮想静的メソッドが必要です。私は私が持つことができないことを知っています。

どうやら貧弱なデザイン-誰かが私がそれをやり直すのを手伝ってくれる?インスタンスの継承を使用して仮想メソッドを適切に使用できることはわかっていますが(このように機能させています)、クライアントコードは常にインスタンスを作成する必要があります(またはシングルトンだと思います)。

4

2 に答える 2

4

これはあなたの目的に役立つかもしれませんが、私は確かにいくつかの例外処理を含め、それがなぜそしてどのように機能するかについての大量のドキュメントをその実装に伴います。

の静的コンストラクターBaseが(1回)実行されると、現在アプリドメインにロードされているすべてのアセンブリがカタログ化され、から派生するタイプが選択されBaseます。それらを繰り返して、静的コンストラクターを実行します。ただし、これにより、各実装のcctorが1回だけ実行されることが保証されなくなったことに注意してください。そのアサーションを再作成するには、各実装にロジックを追加する必要があります。さらに、cctor forBaseが実行された後にロードされる型は、のメソッドの呼び出しによって初期化されません。Base

仮想メソッドをシミュレートするには、newキーワードを使用して基本メソッドを非表示にします。宣言しているクラスの名前で修飾することにより、基本メソッドを呼び出すことができます(B例のクラスのように)

using System;
using System.Linq;
using System.Runtime.CompilerServices;

namespace ConsoleApplication6
{
    public class Base
    {
        static Base()
        {
            Console.WriteLine("Base cctor");

            var thisType = typeof (Base);
            var loadedTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes());
            var derivations = loadedTypes.Where(thisType.IsAssignableFrom);

            foreach(var derivation in derivations)
            {
                RuntimeHelpers.RunClassConstructor(derivation.TypeHandle);
            }
        }

        public static void Foo()
        {
            Console.WriteLine("Bar");
        }
    }

    public class A : Base
    {
        static A()
        {
            Console.WriteLine("A cctor");
        }
    }

    public class B : Base
    {
        static B()
        {
            Console.WriteLine("B cctor");
        }

        public new static void Foo()
        {
            Console.WriteLine("Bar!!");
            Base.Foo();
        }
    }

    class Program
    {
        static void Main()
        {
            Console.WriteLine("A:");
            A.Foo();
            Console.WriteLine();
            Console.WriteLine("B:");
            B.Foo();
            Console.WriteLine();
            Console.WriteLine("Base:");
            Base.Foo();
            Console.ReadLine();
        }
    }
}

編集

別のオプションは、CRTP(またはC#パラダイムのCRGP)または不思議なことに繰り返されるテンプレート(ジェネリック)パラメーターパターンにあります

using System;
using System.Runtime.CompilerServices;

namespace ConsoleApplication6
{
    public class Base<T>
        where T : Base<T>
    {
        static Base()
        {
            RuntimeHelpers.RunClassConstructor(typeof (T).TypeHandle);
        }

        public static void Foo()
        {
            Console.WriteLine("Bar");
        }
    }

    public class Base : Base<Base>
    {
    }

    public class A : Base<A>
    {
        static A()
        {
            Console.WriteLine("A cctor");
        }
    }

    public class B : Base<B>
    {
        static B()
        {
            Console.WriteLine("B cctor");
        }

        public new static void Foo()
        {
            Console.WriteLine("Bar!!");
            Base<B>.Foo();
        }
    }

    class Program
    {
        static void Main()
        {
            Console.WriteLine("A:");
            A.Foo();
            Console.WriteLine();
            Console.WriteLine("B:");
            B.Foo();
            Console.WriteLine();
            Console.WriteLine("Base:");
            Base.Foo();
            Console.ReadLine();
        }
    }
}

この場合、静的メソッドを呼び出すと、A実際にはメソッドが呼び出された方法Base<A>とは異なるBase<B>か、メソッドが呼び出された方法を判別して適切なcctorを実行できBaseます

于 2012-10-22T05:43:49.850 に答える
2

Genericsを使用してこれを実現できます。たとえば、次のようなものを使用できます。

public class MainStatic<T> where T : MainStatic<T>
{
    public static void Foo()
    {
    }

    static MainStatic()
    {
        RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
    }
}

public class SubStatic : MainStatic<SubStatic>
{
    public static void Bar()
    {
    }
}

public class Instance
{
    public void FooBar()
    {
        SubStatic.Foo();
        SubStatic.Bar();
    }
}
于 2012-10-22T07:22:26.110 に答える