インターフェイスを実装する型に対してパラメーターのないコンストラクターを要求するにはどうすればよいでしょうか?

StackOverflow https://stackoverflow.com/questions/26903

質問

方法はありますか?

特定のインターフェイスを実装するすべての型にパラメーターなしのコンストラクターを持たせる必要がありますが、それは可能ですか?

私は社内の他の開発者が特定のプロジェクトで使用するための基本コードを開発しています。

特定のタスクを実行する型のインスタンスを (別のスレッドで) 作成するプロセスがあり、それらの型が特定のコントラクト (つまりインターフェイス) に従う必要があります。

インターフェイスはアセンブリの内部にあります

インターフェイスを使用しないこのシナリオについての提案があれば、喜んで検討させていただきます...

役に立ちましたか?

解決

フアン・マヌエルはこう語った。

それがインターフェースのコントラクトの一部にできない理由が理解できない理由の 1 つです

それは間接的なメカニズムです。ジェネリックを使用すると、インターフェイスとともに型情報を「不正」して送信することができます。ここで覚えておくべき重要な点は、制約は直接作業しているインターフェイスには適用されないということです。これはインターフェイス自体に対する制約ではなく、インターフェイス上で「同調する」他のタイプに対する制約です。申し訳ありませんが、これが私が提供できる最善の説明です。

この事実を説明するために、私が aku のコードで気づいた穴を指摘します。コンパイルは問題なく行われるが、インスタンス化しようとすると実行時に失敗するクラスを作成することも可能です。

public class Something : ITest<String>
{
  private Something() { }
}

何かが ITest<T> から派生していますが、パラメーターなしのコンストラクターは実装されていません。String はパラメーターなしのコンストラクターを実装しているため、正常にコンパイルされます。繰り返しますが、制約は ITest や何かではなく T、つまり String にあります。T の制約が満たされているため、これはコンパイルされます。しかし、実行時には失敗します。

防ぐために いくつかの この問題が発生した場合は、以下のように別の制約を T に追加する必要があります。

public interface ITest<T>
  where T : ITest<T>, new()
{
}

新しい制約に注目してください。T :ITest<T>。この制約は、ITest<T> の引数パラメーターに渡す内容を指定します。 しなければならない また 派生する ITest<T> から。

それでもこれでは防げない 全て 穴のケース。A にはパラメーターのないコンストラクターがあるため、以下のコードは正常にコンパイルされます。ただし、B のパラメーターなしのコンストラクターはプライベートであるため、プロセスで B をインスタンス化すると実行時に失敗します。

public class A : ITest<A>
{
}

public class B : ITest<A>
{
  private B() { }
}

他のヒント

率直に言ってはいけないが、あなたはインターフェースの目的を誤解している。

インターフェイスとは、複数の人がそれを独自のクラスに実装し、それらのクラスのインスタンスを他のクラスに渡して使用できることを意味します。創造は不必要な強い結合を生み出します。

インターフェイスを実装する使用可能なクラスのインスタンス、またはリクエストに応じて上記のアイテムを作成できるファクトリのインスタンスを登録させるために、ある種の登録システムが本当に必要なようです。

ファン、

残念ながら、強く型付けされた言語ではこれを回避する方法はありません。コンパイル時に、Activator ベースのコードによってクラスをインスタンス化できるかどうかを確認することはできません。

(編集:誤った代替ソリューションを削除しました)

その理由は、残念ながら、インターフェイス、抽象クラス、または仮想メソッドをコンストラクターまたは静的メソッドと組み合わせて使用​​することができないためです。簡単に言うと、前者には明示的な型情報が含まれておらず、後者には明示的な型情報が必要であるためです。

コンストラクターと静的メソッド しなければならない 呼び出し時に利用可能な明示的な (コード内で) 型情報が存在します。これが必要なのは、ランタイムが実際に呼び出す具体的なメソッドを決定するために必要な、基になる型を取得するためにランタイムによってクエリできる、関連するクラスのインスタンスがないためです。

インターフェイス、抽象クラス、または仮想メソッドの重要な点は、関数呼び出しを実行できることです。 それなし これは、呼び出し元のコードから直接利用できない「隠された」型情報を持つ参照対象のインスタンスが存在するという事実によって可能になります。したがって、これら 2 つのメカニズムは単純に相互排他的です。これらを混在させると、最終的にどこにも具体的な型情報がまったくなくなってしまうため、一緒に使用することはできません。つまり、ランタイムは呼び出しを要求している関数がどこにあるのかわかりません。

使用できます 型パラメータ制約

interface ITest<T> where T: new()
{
    //...
}

class Test: ITest<Test>
{
    //...
}

したがって、必要なのは、 もの インターフェイスを実装する未知の型のインスタンスを作成する可能性があります。基本的に次の 3 つのオプションがあります。ファクトリ オブジェクト、Type オブジェクト、またはデリゲート。与えられるものは次のとおりです。

public interface IInterface
{
    void DoSomething();
}

public class Foo : IInterface
{
    public void DoSomething() { /* whatever */ }
}

Type の使用は非常に醜いですが、いくつかのシナリオでは意味があります。

public IInterface CreateUsingType(Type thingThatCreates)
{
    ConstructorInfo constructor = thingThatCreates.GetConstructor(Type.EmptyTypes);
    return (IInterface)constructor.Invoke(new object[0]);
}

public void Test()
{
    IInterface thing = CreateUsingType(typeof(Foo));
}

最大の問題は、コンパイル時に Foo が実際に実行されるという保証がないことです。 もっている デフォルトのコンストラクター。また、パフォーマンスが重要なコードの場合、リフレクションは少し遅くなります。

最も一般的な解決策は、ファクトリを使用することです。

public interface IFactory
{
    IInterface Create();
}

public class Factory<T> where T : IInterface, new()
{
    public IInterface Create() { return new T(); }
}

public IInterface CreateUsingFactory(IFactory factory)
{
    return factory.Create();
}

public void Test()
{
    IInterface thing = CreateUsingFactory(new Factory<Foo>());
}

上記の中で、本当に重要なのは IFactory です。Factory は、次のようなクラスの単なる便宜クラスです。 する デフォルトのコンストラクターを提供します。これは最も単純で、多くの場合最適な解決策です。

現在は一般的ではありませんが、今後さらに一般的になる可能性が高い 3 番目のソリューションは、デリゲートを使用することです。

public IInterface CreateUsingDelegate(Func<IInterface> createCallback)
{
    return createCallback();
}

public void Test()
{
    IInterface thing = CreateUsingDelegate(() => new Foo());
}

ここでの利点は、コードが短くてシンプルであり、 どれでも (クロージャを使用すると) オブジェクトの構築に必要な追加データを簡単に渡すことができます。

型を指定して RegisterType メソッドを呼び出し、ジェネリックスを使用して制約します。そうすれば、アセンブリを歩いて ITest 実装者を見つけるのではなく、実装者を保存し、そこから作成するだけです。

void RegisterType<T>() where T:ITest, new() {
}

私はそうは思わない。

これに抽象クラスを使用することもできません。

皆さんに次のことを思い出していただきたいと思います。

  1. .NET での属性の記述は簡単です
  2. 社内標準への準拠を保証する静的分析ツールを .NET で簡単に作成できます。

特定のインターフェイスを実装する/属性を持つすべての具象クラスを取得するツールを作成し、パラメーターのないコンストラクターがあることを確認するには、コーディング作業に約 5 分かかります。これをビルド後のステップに追加すると、実行する必要があるその他の静的解析のためのフレームワークが完成します。

言語、コンパイラー、IDE、脳、それらはすべてツールです。使ってください!

いいえ、それはできません。おそらくあなたの状況では、ファクトリインターフェイスが役立つでしょうか?何かのようなもの:

interface FooFactory {
    Foo createInstance();
}

Foo の実装ごとに、その作成方法を知っている FooFactory のインスタンスを作成します。

Activator がクラスをインスタンス化するためにパラメーターなしのコンストラクターは必要ありません。パラメーター化されたコンストラクターを使用して、アクティベーターからすべてのパラメーターを渡すことができます。チェックアウト これに関するMSDN.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top