25

(子) クラスに、C# または Java で特定のシグネチャまたは特定の静的メソッドを持つコンストラクターを強制する方法はありますか?

明らかに、これにインターフェイスを使用することはできません。使用が制限されることはわかっています。私が役に立つと思う例の 1 つは、次のようなデザイン ガイドラインを適用する場合です。

例外
すべてに 4 つの標準コンストラクターが必要ですが、それを強制する方法はありません。これらをキャッチするには、FxCop (C# の場合) などのツールに頼る必要があります。

演算子
2 つのクラスを合計できることを指定する規約はありません (C# の operator+ を使用)。

この制限を回避する設計パターンはありますか? C# または Java の将来のバージョンでこの制限を克服するために、言語に追加できる構成要素は何ですか?

4

9 に答える 9

11

ジェネリックスを使用すると、型引数にパラメーターなしのコンストラクターを強制できますが、それは限界です。

ジェネリックス以外では、これらの制限が存在する場合でも実際に使用するのは難しいでしょうが、型パラメーター/引数に役立つ場合があります。インターフェイス(または場合によっては静的インターフェイス)で静的メンバーを許可すると、同様に「一般的な数値演算子」の問題に役立つ可能性があります。

同様の問題に直面したとき、私は少し前にこれについて書きました。

于 2008-10-02T07:56:52.717 に答える
9

コンパイル時に強制されるわけではありませんが、同様の問題を調べるのに多くの時間を費やしました。ジェネリック対応の数学ライブラリと効率的な (デフォルトではない) ctor API の両方が MiscUtil で利用できます。ただし、これらは実行時の最初の使用時にのみチェックされます。実際には、これは大きな問題ではありません。単体テストでは、欠落しているオペレーター/ctor が非常に迅速に検出されるはずです。しかし、それは機能し、非常に迅速に...

于 2008-10-02T12:31:08.033 に答える
7

Factory パターンを使用できます。

interface Fruit{}

interface FruitFactory<F extends Fruit>{
   F newFruit(String color,double weight);

   Cocktail mixFruits(F f1,F f2);
}

その後、あらゆる種類のフルーツのクラスを作成できます

class Apple implements Fruit{}
class AppleFactory implements FruitFactory<Apple>{
   public Apple newFruit(String color, double weight){
       // create an instance
   }
   public Cocktail mixFruits(Apple f1,Apple f2){
       // implementation
   }
}

これは、Factory を使用する以外の方法でインスタンスを作成できないことを強制するものではありませんが、少なくとも Factory から要求するメソッドを指定することはできます。

于 2008-10-02T08:37:26.783 に答える
4

強制コンストラクタ

できません。最も近いのは、デフォルトのコンストラクターをプライベートにしてから、パラメーターを持つコンストラクターを提供することです。しかし、それにはまだ抜け穴があります。

class Base
{
  private Base() { }
  public Base(int x) {}
}

class Derived : Base
{
  //public Derived() { } won't compile because Base() is private
  public Derived(int x) :base(x) {}
  public Derived() : base (0) {} // still works because you are giving a value to base
}
于 2008-10-02T07:58:19.717 に答える
1

言語の問題は、静的メソッドが実際には二級市民であるということです(コンストラクターも一種の静的メソッドであり、最初からインスタンスを必要としないためです)。

静的メソッドは、名前空間を持つ単なるグローバルメソッドであり、定義されているクラスに実際には「属していません」(OK、クラス内のプライベート(静的)メソッドにアクセスできますが、それだけです)。

コンパイラレベルでの問題は、クラスインスタンスがないと、仮想関数テーブルがないことです。つまり、すべての継承とポリモーフィズムを使用することはできません。

クラスごとにグローバル/静的仮想テーブルを追加することで機能させることができると思いますが、まだ実行されていない場合は、おそらくそれには十分な理由があります。

于 2008-10-02T07:57:04.323 に答える
1

ここで、私が言語設計者だったら解決します。

インターフェイスに静的メソッド、演算子、およびコンストラクターを含めることができます。

interface IFoo  
{  
  IFoo(int gottaHaveThis);  
  static Bar();  
}

interface ISummable
{
      operator+(ISummable a, ISummable b);
}

対応するnew IFoo(someInt)またはIFoo.Bar()

コンストラクターの継承を許可します (静的メソッドと同様)。

class Foo: IFoo
{
  Foo(int gottaHaveThis) {};
  static Bar() {};
}

class SonOfFoo: Foo 
{
  // SonOfFoo(int gottaHaveThis): base(gottaHaveThis); is implicitly defined
}

class DaughterOfFoo: Foo
{
  DaughhterOfFoo (int gottaHaveThis) {};
}

プログラマーがインターフェイスにキャストできるようにし、必要に応じて、クラスで明示的に指定されていなくても、キャストが意味的に有効かどうかを実行時に確認します。

ISummable PassedFirstGrade = (ISummable) 10; 
于 2008-10-02T10:13:46.960 に答える
1

残念ながら、C# ではできません。ただし、ここにパンチがあります。

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Foo.Instance.GetHelloWorld());
        Console.ReadLine();
    }
}

public class Foo : FooStaticContract<FooFactory>
{
    public Foo() // Non-static ctor.
    {
    }

    internal Foo(bool st) // Overloaded, parameter not used.
    {
    }

    public override string GetHelloWorld()
    {
        return "Hello World";
    }
}

public class FooFactory : IStaticContractFactory<Foo>
{
    #region StaticContractFactory<Foo> Members

    public Foo CreateInstance()
    {
        return new Foo(true); // Call static ctor.
    }

    #endregion
}

public interface IStaticContractFactory<T>
{
    T CreateInstance();
}

public abstract class StaticContract<T, Factory>
    where Factory : IStaticContractFactory<T>, new() 
    where T : class
{
    private static Factory _factory = new Factory();

    private static T _instance;
    /// <summary>
    /// Gets an instance of this class. 
    /// </summary>
    public static T Instance
    {
        get
        {
            // Scary.
            if (Interlocked.CompareExchange(ref _instance, null, null) == null)
            {
                T instance = _factory.CreateInstance();
                Interlocked.CompareExchange(ref _instance, instance, null);
            }
            return _instance;
        }
    }
}

public abstract class FooStaticContract<Factory>
    : StaticContract<Foo, Factory>
    where Factory : IStaticContractFactory<Foo>, new() 
{
    public abstract string GetHelloWorld();
}
于 2008-10-02T12:15:38.477 に答える
0

ええと、あなたの質問の文言から、コンパイル時の強制を探していることがわかります。他の誰かが、コンパイラがすべきことをほのめかしている方法でこれを行うことを可能にする素晴らしい提案/ハックを持っていない限り、これを行うカスタム MSbuild タスクを作成することをお勧めします。PostSharp のような AOP フレームワークは、ビルド タスク モデルをピギー バックすることで、コンパイル時にこれを達成するのに役立つ場合があります。

しかし、コード分析や実行時の強制の何が問題になっているのでしょうか? 多分それは単なる好みであり、私はそれを尊重しますが、個人的にはCA / FXCopにこれらのことをチェックさせることに問題はありません...そして、クラスの下流の実装者にコンストラクター署名を強制したい場合は、いつでも実行するルールを追加できます-リフレクションを使用した基本クラス コンストラクターでの時間チェック。

リチャード

于 2008-10-02T08:02:38.303 に答える
0

あなたが何を達成しようとしているのかよくわかりませんが、詳しく教えてください。特定のコンストラクターまたは静的メソッドを異なるクラス間で強制する唯一の理由は、実行時にそれらを動的に実行しようとすることです。これは正しいですか?

コンストラクターは、クラスの特定のニーズを初期化することを目的としているため、特定のクラスに固有であることを目的としています。私が理解しているように、クラス階層またはインターフェイスで何かを強制したい理由は、それが実行中のプロセスに関連するアクティビティ/操作であるためですが、状況によって異なる場合があります。これは、静的メソッドを使用して達成できないポリモーフィズムの意図された利点であると私は信じています。

また、静的メソッドを呼び出したいクラスの特定のタイプを知る必要があります。これにより、インターフェイスまたは抽象クラスが達成しようとしている動作の違いのポリモーフィックな隠蔽がすべて破られます。

コンストラクターによって表される動作が、これらのクラスのクライアント間のコントラクトの一部であることが意図されている場合は、それをインターフェイスに明示的に追加します。

クラスの階層に同様の初期化要件がある場合は、抽象基本クラスを使用しますが、そのコンストラクターのパラメーターをどのように見つけるかは、継承クラスに依存する必要があります。これには、類似または同一のコンストラクターの公開が含まれる場合があります。

これが実行時にさまざまなインスタンスを作成できるようにすることを目的としている場合は、すべての具象クラスのさまざまなニーズを認識している抽象基本クラスで静的メソッドを使用することをお勧めします (これには依存性注入を使用できます)。

于 2008-10-02T12:15:51.470 に答える