2

1 つのインターフェイスInterfaceBaseと、そこから派生したいくつかのインターフェイスがありますInterface1, Interface2。次に、基本インターフェイスではなく、インターフェイスを実装するクラスがありInterfaceXます。

今、私はジェネリックの初心者であり、これの非常に多くの新しいアプローチが私の頭の中で大きな混乱を引き起こしました:( 。私は次のようなものを呼び出すファクトリ(静的クラス)を作成したい

Interface1 concrete1 = Factory.Get<Interface1>();

これが機能しない私の(サンプル)実装です:

  public static class Factory {

    public static T Get<T>() where T: InterfaceBase{

      Type type = typeof(T);

      //return new Concrete1() as T; // type T cannot be used with the as
      //return new Concrete1() as type; //type not found
      //return new Concrete1(); // cannot implicitly convert
      //return new Concrete1() as InterfaceBase; //cannot convert IBase to T
      //return new Concrete1() as Interface1; //cannot convert Interface1 to T
    }
  }

私が達成したいのは、アプリケーションの残りの部分からクラス (Web サービス ハンドラー) を非表示にして、軽く交換することです。クラスはシングルトンになり、ファクトリ内のディクショナリに格納されるため、ファクトリを使用したかったので、ファクトリはこのメソッドを介してアプリケーション全体にそれらを広げることができますが、インターフェイスとして..多分私は制約を正しく使用していません何か間違ったことをしていますか?私のアプローチは悪いですか?より良いものはありますか? アーキテクチャ全体を作り直す必要があるのでしょうか? アーキテクチャをよりよく示す図。工場はその中にありません

4

5 に答える 5

7

あなたが探しているのは「貧乏人の依存性注入」だと思います。そのためには実際の IoC コンテナーを使用する必要があると思います。多くのオプションがあります (Unity、Castle Windsor、Ninject など)。

とにかく、自分でやりたいと主張する場合は、@Sergey Kudriavtsevが推奨しているものを使用してください。インターフェイスごとに、適切な具象クラスを返すようにしてください。このようなもの:

public interface InterfaceBase { }
public interface Interface1 : InterfaceBase { }
public interface InterfaceX : InterfaceBase { }

public class Concrete1 : Interface1 { }
public class ConcreteX : InterfaceX { }

public static class Factory
{
    public static T Get<T>()
        where T : InterfaceBase
    {
        if (typeof(Interface1).IsAssignableFrom(typeof(T)))
        {
            return (T)(InterfaceBase)new Concrete1();
        }
        // ...
        else if (typeof(InterfaceX).IsAssignableFrom(typeof(T)))
        {
            return (T)(InterfaceBase)new ConcreteX();
        }

        throw new ArgumentException("Invalid type " + typeof(T).Name, "T"); // Avoids "not all code paths return a value".
    }
}

そして、インターフェイス参照をファクトリに渡すことで呼び出します。

var instance = factory.Get<Interface1>();
于 2012-02-09T15:22:01.223 に答える
2

これはうまくいくはずです:

return (T)(new Concrete1());

また、ファクトリ メソッドを呼び出すコードは次のようになります。

Interface1 concrete1 = Factory.Get<Interface1>();
于 2012-02-09T15:10:23.393 に答える
2

あなたの質問の一部に答えるには:

return new Concrete1() as T; // type T cannot be used with the as 

は参照型として認識されていないため、型Tを使用できません。制約を使用して、クラスを参照型に制約できます。もちろん、値型でこのメソッドを使用できる必要がないことを前提として、これにより、演算子を使用できるようになります。asTwhere T : class [, ...]as

編集

そうは言っても、私はrsennaの答えを好みます。コンパイル時に直接キャストが機能することがわかっているため、直接キャストを使用する方がより理にかなっていますas また、「実際の」IoC コンテナーを調査するという彼の推奨にも同意しますが、ジェネリックについて深く理解することは、ジェネリックについて学ぶときに非常に役立つことを付け加えておきます。あなたはジェネリックの初心者だと言っているので、このような演習は、ジェネリックについて学ぶのに役立ち、IoC コンテナーが追加できる価値をよりよく理解するのに役立つため、おそらく良い考えです。

編集2

T を InterfaceBase に制約し、Concrete1 を T にキャストします。Concrete1 が T から派生することは知られていません。asもちろん、これがキャストを使用する理由です。答えは、class制約を追加すれば問題ないはずです。

編集3

rsenna が指摘しているように、アップキャストとダウンキャストで実行時の型チェックを取得することもできます。

return (T)(object)new Concrete1();

また

return (T)(InterfaceBase)new Concrete1();

これが効率の点でどのように比較されるのだろうか

return new Concrete1() as T;

時間があれば今日中に確認してみます。

于 2012-02-09T15:25:07.657 に答える
1

このようなことをする

public static class Factory {

public static T Get<T>() where T: InterfaceBase{

  return (T) new Concrete1();
}

安全に入力できません。メソッドがT==Concrete1で呼び出されることを保証することはできません。TはInterfaceBaseの任意のサブタイプにすることができ、Concrete1はそれらのサブタイプの1つであり、必ずしも同じTである必要はありません。したがって、コンパイラはTにキャストすることを許可しません。これは、文字列またはその他にキャストすることを許可しないのと同じ方法です。他の無関係なタイプ。

Activator.CreateInstance()は、それを処理する1つの方法です。CreateInstanceは、ビルドされたインスタンスがメソッドの期待される出力値であるタイプTであることを保証します。

于 2012-02-09T15:54:07.807 に答える
0

T が InterfaceBase のサブタイプ (T : InterfaceBase) であることがわかっている場合は、Get メソッドの戻り値の型を InterfaceBase にすることができます。

public static InterfaceBase Get<T>() where T : InterfaceBase
{        
    return new Concrete1();
}

このメソッドは、InterfaceBase のサブインターフェイスを T として使用して呼び出すことができます。

InterfaceBase ib = Factory.Get<Interface1>();

T として InterfaceBase のサブインターフェイス以外のものを渡すと、コンパイラは文句を言います。

これはより良いアプローチだと思いますが、@phoog が指摘しているように、T 型パラメーターの具象クラスでのみ機能します。

public static T Get<T>() where T : InterfaceBase
{
    Type type = typeof(T);
    if (t.IsAbstract || t.IsInterface)
{
        throw new ArgumentException(@"Only non-abstract classes supported as T type parameter.");
}
    return Activator.CreateInstance<T>();
}
于 2012-02-09T15:21:21.773 に答える