C# 带有类型参数的泛型工厂

C# 带有类型参数的泛型工厂,c#,C#,我有以下情况 我的工厂类需要根据CreateStrategy函数的输入字符串参数创建适当的策略对象 Strategy1、Strategy2等都是从公共Strategy基类派生的。但是,每个策略都有不同的验证机制,这是Factory类的类型参数。但是,StrategyValidator不是任何常见类型,具有不同的接口 因此,在下面的代码中,我无法指定StrategyValidator类型的任何公共约束 我是C#的新手,因此不确定是否存在克服这个设计问题的机制。请建议 public class Fa

我有以下情况

我的工厂类需要根据CreateStrategy函数的输入字符串参数创建适当的策略对象

Strategy1、Strategy2等都是从公共Strategy基类派生的。但是,每个策略都有不同的验证机制,这是Factory类的类型参数。但是,StrategyValidator不是任何常见类型,具有不同的接口

因此,在下面的代码中,我无法指定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;
}
我得到的错误是

类型“T”不能用作泛型类型中的类型参数“T” 或方法“TestCSharp.Product1”。没有拳击转换或拳击比赛 从“T”到“TestCSharp.S1”的类型参数转换


我认为在这种情况下,答案是,它取决于你想要什么
产品
策略
。你似乎想把你的逻辑分成两个分支。然后,您希望通过使用泛型将其再次耦合,但正如您所注意到的,它将不起作用

考虑一个场景,与上面的场景类似——但是每个实现
IStrategy
的类都有一个而不是两个方法,这会产生副作用(即打印一个字符串)。当允许的类型范围有共同点时,可以使用泛型。在我刚才提到的例子中,两者都有一个返回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
的约束。因此,
工厂
变成:

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

否则就不可能了,就像你说的那样,因为
S1
S2
没有共同的祖先可以被您的
产品
类接受。

我并不完全理解您的场景,但据我所知,您使用的工厂模式必须使用反射来实例化产品。这有点难看,因为它没有给消费者任何关于什么策略类型可以用于给定产品名称的提示

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);
    }
}
公共类工厂
{
公共产品库创建(字符串名称)
{
类型;
交换机(名称)
{
案例“P1”:
类型=类型(产品1);
打破
案例“P2”:
类型=类型(产品2);
打破
案例“P3”:
类型=类型(产品3);
打破
违约:
返回null;
}
type=type.MakeGenericType(typeof(T));
return(ProductBase)Activator.CreateInstance(类型);
}
}

您可以将函数更改为将StrategyValidator作为类型


您是否考虑过重载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>();
        }
    }
}

这可能有助于缓解您的一些头痛问题。

我对您的意图有点困惑,您能否添加一些您希望如何使用工厂的代码?我希望传递与不同策略对象相关的不同验证类型。CreateStrategy只是帮助我用用户指定的验证类实例化适当的策略对象,而且由于这些策略使用完全不同的机制来实现相同的目标,因此它们的验证是唯一的,没有通用性,在generalIm中,我特别注意查看使用代码,因为我不太清楚为什么这里的泛型是在类级别而不是方法级别。另一方面,也许它们的共同点在于它们是一种策略?@Chubsdad,我不明白。如果您可以编辑
ABCDValidator
WMIValidator
难道您不能简单地给它们一个共同的祖先类/接口,即使是空的,然后将其用作约束?在我的例子中,即使我有一个共同的函数ExecuteLogic,它也会为每个策略类接受不同的参数。另外,请查看最新的代码帖子,关于Product1和Product2的约束,我希望分别是S1和S2,而不是IStrategy@Chubsdad我想说的是,在这种情况下,您只需从IStrategy中删除ExecuteLogic方法(使其为空)但是,继续把IStrategy作为课堂上的装饰,以表明它是一种策略。这提高了可读性,并使我们的策略更加安全(?)。@Chubsdad正如上面的评论员所说,如果
Product1
被限制在
S1
Product2
S2
,我看不到使用泛型会产生替代方案的情况(即使可能)将产品与其各自的战略结合起来。它还可以为您省去使用
IStrategy
作为接口的麻烦,因为正如您所说,不同的策略具有不同的特征。您可以将整个逻辑放在产品中。在我的例子中,S1和S2是接口,我可以使用不同于这些接口的策略并配置我的产品类。这将生成将产品与策略类Product1:ProductBase组合的备选方案,其中T:S1,new(){void g(){T T T=new T();T.f1();};
public ProductBase Create<T1, T2>(String Type) where T1 : S1 where T2 : S2
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);
    }
}
public static StrategyBase CreateStrategy<StrategyValidator>(String Type)
public static StrategyBase CreateStrategy<T>(String Type) where T:StrategyValidator
    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>
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>();
        }
    }
}
class ProductBase<T> : IProductBase where T : IStrategy { }