2

次のような状況があります。

私の Factory クラスは、CreateStrategy 関数への入力文字列引数に基づいて、適切な Strategy オブジェクトを作成する必要があります。

Strategy1、Strategy2 などはすべて、共通の StrategyBase クラスから派生しています。ただし、各戦略には、Factory クラスの型パラメーターである異なる検証メカニズムがあります。ただし、StrategyValidators は一般的なタイプではなく、異なるインターフェイスを持っています。

したがって、以下のコードでは、StrategyValidator 型に共通の制約を指定できません。

私は C# を初めて使用するため、この設計上の問題を解決するメカニズムが存在するかどうかはわかりません。提案してください

public class Factory
{
    //Create the appropriate Concrete Implementation class based on the type
    public static StrategyBase CreateStrategy<StrategyValidator>(String Type)
    {
        StrategyBase EnumImp = null;

        // WMI based implementation
        if (Type == "Type1")
        {
            s = Strategy1<StrategyValidator>.Instance;
        }
        else if (Type = "Type2")
        {
            s = Strategy2<StrategyValidator>.Instance;
        }
        return s;
    }

    private StrategyBase s;
}

使用目的はこちら

Factory f = new Factory(); 

f.CreateStrategy<WMIValidator>("WMI");
f.CreateStrategy<ABCDValidator>("ABCD");

ここでWMIValidator、 とABCDValidatorは関連のない型ですが、関数によって作成される実際のクラスはCreateStrategy、共通のベースを持つなど、階層内で関連していますStrategyBase

問題を説明するためのサンプルコードを次に示します

namespace TestCSharp
{
    public interface IStrategy
    {
    };

    public interface S1 : IStrategy
    {
        void f1();
        void f2();
    };

    public class S1Concrete : S1
    {
        public void f1() { }
        public void f2() { }
    }

    public interface S2 : IStrategy
    {
        void f3();
        void f4();
    };

    public class S2Concrete : S2
    {
        public void f3() { }
        public void f4() { }
    };

    public interface ProductBase
    {
    };

    class Product1<T> : ProductBase where T : S1
    {
    };

    class Product2<T> : ProductBase where T : S2
    {
    };

    public class Factory
    {
        public ProductBase Create<T>(String Type)
        {
            if (Type == "P1")
                return new Product1<T>();
            else if (Type == "P2")
                return new Product2<T>();
        }
    };

    class Program
    {
        static void Main(string[] args)
        {
            Factory f = new Factory();
            ProductBase s = f.Create<S1Concrete>("Type1");
        }
    }
}

私が得るエラーは

型 'T' は、ジェネリック型またはメソッド 'TestCSharp.Product1' の型パラメーター 'T' として使用できません。'T' から 'TestCSharp.S1' へのボックス変換や型パラメーターの変換はありません。

4

4 に答える 4

2

私はあなたのシナリオを完全には理解していませんが、あなたが使用しているファクトリパターンを知る限り、リフレクションを使用して製品をインスタンス化する必要があります。これは、特定の製品名で使用できる戦略タイプについてのヒントを消費者に提供しないため、少し醜いです。

public class Factory
{
    public ProductBase Create<T>(string name)
    {
        Type type;
        switch (name)
        {
            case "P1":
                type = typeof (Product1<>);
                break;
            case "P2":
                type = typeof (Product2<>);
                break;
            case "P3":
                type = typeof (Product3<>);
                break;
            default:
                return null;
        }
        type = type.MakeGenericType(typeof (T));
        return (ProductBase) Activator.CreateInstance(type);
    }
}
于 2013-01-11T03:52:52.350 に答える
1

この場合の答えは、あなたが何をしたいのか、何をしたいのかによるProductStrategy思います。あなたがやろうとしているように見えるのは、ロジックを2つのブランチに分割することです。次に、ジェネリックを使用して再度結合する必要がありますが、お気づきのとおり、機能しません。

上記のシナリオと同様のシナリオを考えてみましょう。ただし、実装する各クラスIStrategyに、副作用(つまり、文字列を出力する)を行う2つのメソッドではなく1つのメソッドがあります。許可される型の範囲に共通点がある場合は、ジェネリックを使用します。先ほど触れた場合、どちらにもvoidを返し、パラメーターを受け入れないメソッドがあります。IStrategyたとえば、にメソッドを追加できます。

public interface IStrategy
{
    void ExecuteLogic();
};

public class S1 : IStrategy
{
    public void ExecuteLogic()
    {
        OneMethod();
    }

    void OneMethod()
    {
        Console.WriteLine("Hello");
    }
};

public class S2 : IStrategy
{
    public void ExecuteLogic()
    {
        TotallyDifferentMethod();
    }

    void TotallyDifferentMethod()
    {
        Console.WriteLine("World");
    }
};

さて、あなたもそう言って、異なる検証メカニズムStrategy1を持っています。Strategy2ただし、同じメソッドとコンテキスト(したがって同じパラメーターと変数)でそれらを使用しているように思われるので、それらを類似させる何かが必要です。それでも、IStrategy必要な方法で定義したので、それをの制約として使用できますCreate<T>。したがって、次のようにFactoryなります。

public class Factory
{
    public ProductBase Create<T>(String Type) where T : IStrategy
    {
        if (Type == "P1")
            return new Product1<T>();
        else if (Type == "P2")
            return new Product2<T>();
        return null;
    }
};

しかし、まだ1つのケースがあります。ジェネリック型として呼び出されたくない場合、またはジェネリック型としてProduct1呼び出されたくない場合は、そもそもジェネリックを使用するのはなぜですか?製品を相対的な戦略と簡単に組み合わせることができ、コードを大幅に簡素化することもできます。S2Product2S1

私が何か(または質問全体)を見逃した場合は、コメントを残してください。私は私の答えを適応させようとします。

編集:あなたは今あなたの例を再定義し、インターフェースとして使用S1S2ているので、私はあなたが何を意味するのかを見ることができます。方法は、の複数のジェネリック型と制約を定義することですFactory.Create。例:

public ProductBase Create<T1, T2>(String Type) where T1 : S1 where T2 : S2

あなたが適切に述べたように、それ以外の場合は不可能です。なぜなら、あなたのクラスに共通の祖先がなくS1S2それを受け入れることができるからです。Product

于 2013-01-11T03:49:29.773 に答える
0

関数を変更して、タイプとして StrategyValidator を取ることができます。

から

public static StrategyBase CreateStrategy<StrategyValidator>(String Type)

public static StrategyBase CreateStrategy<T>(String Type) where T:StrategyValidator

あなたの質問に答えるために、条件付きチェックを避けることはできません。

コードを簡素化するために、さまざまな組み合わせ ("Type1"、"Type2" など) をいずれかdictionaryまたは構成に移動するDependency Injectionことができます。

例。

    if (!dict.ContainsKey(key)) 
       throw New InvalidArgumentException();

    StrategyBase EnumImp = null;

    var instance = dict[key].MakeGenericType(typeOf(type)).GetProperty("Instance",  BindingFlags.Static | BindingFlags.Public ));   //dict is Dictionary<string, Type>
于 2013-01-11T04:29:30.310 に答える
0

Create<> 関数をオーバーロードすることを検討しましたか? 現在、VisualStudio は手元にありませんが、次のコードはあなたの状況で機能しますか?

namespace ... {
    // ... other code here...

    public class Factory {
        public Product1<T> Create<T>() where T : S1 {
            return new Product1<T>();
        }
        public Product2<T> Create<T>() where T : S2 {
            return new Product2<T>();
        }
    }

    class Program {
        static void Main(string[] args) {
            Factory f = new Factory();
            ProductBase s = f.Create<S1Concrete>();
        }
    }
}

さらに、型の制約をより低いレベルに移動したい場合があります。次のように、抽象ベースの ProductBase クラス (IProductBaseインターフェイスから継承するもの?) を作成することを検討してください。

class ProductBase<T> : IProductBase where T : IStrategy { }

これにより、頭痛の一部が軽減される可能性があります。

于 2013-01-11T06:13:13.720 に答える