10

方法はありますか?

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

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

特定のタスクを実行するタイプのインスタンスを (異なるスレッドで) 作成するプロセスがあり、これらのタイプが特定の契約 (エルゴ、インターフェイス) に従う必要があります。

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

インターフェイスのないこのシナリオについて提案がある場合は、喜んで検討します...

4

10 に答える 10

22

率直に言うと、インターフェースの目的を誤解しています。

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

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

于 2008-08-25T21:19:44.450 に答える
6

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

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

class Test: ITest<Test>
{
    //...
}
于 2008-08-25T22:30:54.340 に答える
5

ファン、

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

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

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

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

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

于 2008-08-25T21:54:47.080 に答える
5

フアン・マヌエルは次のように述べています。

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

間接的な仕組みです。ジェネリックを使用すると、インターフェイスとともに型情報を「チート」して送信できます。ここで覚えておくべき重要なことは、直接操作しているインターフェイスには制約がないということです。これはインターフェイス自体の制約ではなく、インターフェイスに「乗る」他のタイプの制約です。恐れ入りますが、これが私が提供できる最善の説明です。

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

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

何かが ITest<T> から派生していますが、パラメーターなしのコンストラクターを実装していません。String はパラメーターなしのコンストラクターを実装しているため、正常にコンパイルされます。繰り返しになりますが、制約は T にあり、したがって ITest や何かではなく 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() { }
}
于 2008-08-26T05:35:16.880 に答える
4

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

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());
}

ここでの利点は、コードが短くて単純であり、任意の構築方法で機能し、(クロージャーを使用して)オブジェクトの構築に必要な追加データを簡単に渡すことができることです。

于 2008-09-16T00:54:28.360 に答える
1

タイプを指定してRegisterTypeメソッドを呼び出し、ジェネリックスを使用して制約します。次に、アセンブリを歩いてITest実装者を見つける代わりに、それらを保存してそこから作成します。

void RegisterType<T>() where T:ITest, new() {
}
于 2008-08-31T15:36:30.967 に答える
0

私はそうは思わない。

これには抽象クラスも使用できません。

于 2008-08-25T21:10:24.970 に答える
0

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

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

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

言語、コンパイラ、IDE、あなたの脳 - それらはすべてツールです。それらを使用してください!

于 2008-08-25T22:15:37.530 に答える
0

いいえ、それはできません。おそらくあなたの状況では、工場のインターフェースが役に立ちますか? 何かのようなもの:

interface FooFactory {
    Foo createInstance();
}

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

于 2008-08-25T22:31:33.437 に答える
0

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

于 2008-08-25T22:42:16.937 に答える