コンストラクターの署名と静的メソッドをどのように強制しますか?
-
03-07-2019 - |
質問
(子)クラスに、C#またはJavaの特定のシグネチャまたは特定の静的メソッドを持つコンストラクターを強制する方法はありますか
このために明らかにインターフェースを使用することはできません。使用が制限されることはわかっています。私が便利だと思うのは、たとえば次のような設計ガイドラインを実施したい場合です。
例外
それらはすべて4つの標準コンストラクタを持つ必要がありますが、強制する方法はありません。これらをキャッチするには、FxCop(C#の場合)などのツールに依存する必要があります。
演算子
2つのクラスを合計できることを指定する契約はありません(C#でoperator +を使用)
この制限を回避するデザインパターンはありますか? C#またはJavaの将来のバージョンでこの制限を克服するために、言語にどのコンストラクトを追加できますか?
解決
コンパイル時には強制されませんが、私は同じような問題を見るのに多くの時間を費やしました。 汎用の数学ライブラリ、および効率的な(非-default)ctor APIは両方とも MiscUtil で利用できます。ただし、これらは実行時の最初の使用時にのみチェックされます。現実には、これは大きな問題ではありません-単体テストでは、行方不明の演算子/俳優を非常に迅速に見つける必要があります。しかし、それは機能し、非常に迅速に...
他のヒント
ジェネリックを使用すると、型引数にパラメーターなしのコンストラクターを強制することができますが、それでほぼ制限されます。
ジェネリック以外では、これらの制限が存在しても、実際に使用するのは難しいでしょうが、型パラメータ/引数には役立つ場合があります。インターフェイス(または場合によっては静的インターフェイス)で静的メンバーを許可すると、「汎用数値演算子」の使用にも役立ちます。問題。
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
}
}
これは、ファクトリを使用する以外の方法でインスタンスを作成できないことを強制するものではありませんが、少なくとも、ファクトリから要求するメソッドを指定できます。
Forceコンストラクター
できません。最も近い方法は、デフォルトのコンストラクターをプライベートにしてから、パラメーターを持つコンストラクターを提供することです。しかし、まだ抜け穴があります。
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
}
この言語の問題は、静的メソッドが実際には第2クラスの市民であることです(コンストラクターは、インスタンスを必要としないため、静的メソッドの一種でもあります)。
静的メソッドは、名前空間を持つグローバルメソッドであり、実際には「所属」しません。それらが定義されているクラスに(OK、クラス内のプライベート(静的)メソッドにアクセスできますが、それについてです)。
コンパイラレベルの問題は、クラスインスタンスがないと仮想関数テーブルがないため、すべての継承とポリモーフィズムを使用できないことです。
各クラスにグローバル/静的仮想テーブルを追加することで機能させることができると思いますが、まだ実行されていない場合は、おそらく十分な理由があります。
ここで、私が言語設計者であればそれを解決します。
インターフェースに静的メソッド、演算子、およびコンストラクターを含めることを許可します。
interface IFoo
{
IFoo(int gottaHaveThis);
static Bar();
}
interface ISummable
{
operator+(ISummable a, ISummable b);
}
対応する新しい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;
残念ながら、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();
}
まあ、あなたの質問の文言から、あなたがコンパイル時の強制を探しているのは知っています。他の誰かがあなたがコンパイラーが暗示するようにこれを行うことを可能にする素晴らしい提案/ハックを持たない限り、私はあなたがこれを行うカスタムMSbuildタスクを書くことができることを提案します。 PostSharpのようなAOPフレームワークは、ビルドタスクモデルに便乗して簡単にこれを達成するのに役立ちます。
しかし、コード分析または実行時の強制の何が問題になっていますか?多分それは好みであり、私はそれを尊重しますが、私は個人的にCA / FXCopにこれらのことをチェックさせることに問題はありません...そしてあなたが本当にクラスの下流の実装者にコンストラクタシグネチャを持たせることを強制したいなら、あなたはいつでもルールを実行することができます-リフレクションを使用して基本クラスコンストラクターでチェックインする時間。
リチャード
あなたが何を達成しようとしているのかわかりません。詳しく説明してください。異なるクラスに特定のコンストラクターまたは静的メソッドを強制する唯一の理由は、実行時にそれらを動的に実行しようとすることです。これは正しいですか?
コンストラクタは、クラスの特定のニーズを初期化することを目的としているため、特定のクラスに固有であることを目的としています。私が理解しているように、クラス階層またはインターフェースに何かを強制したい理由は、実行されているプロセスに関連するアクティビティ/操作ですが、状況によって異なる場合があるためです。これはポリモーフィズムの意図された利点であり、静的メソッドを使用して達成することはできません。
また、静的メソッドを呼び出すクラスの特定のタイプを知る必要があります。これにより、インターフェイスまたは抽象クラスが達成しようとしている動作の違いの多態的な隠蔽がすべて解除されます。
コンストラクターによって表される動作がこれらのクラスのクライアント間のコントラクトの一部であることを意図している場合、インターフェイスに明示的に追加します。
クラスの階層に同様の初期化要件がある場合、抽象基本クラスを使用しますが、そのコンストラクターのパラメーターの検索方法は継承クラス次第である必要があります。これには、類似または同一のコンストラクターの公開が含まれます。 / p>
これが実行時に異なるインスタンスを作成できるようにする場合、すべての具象クラスの異なるニーズを知っている抽象基本クラスで静的メソッドを使用することをお勧めします(これには依存性注入を使用できます) 。