天道酬勤,学无止境

Generic Factory with type parameter

I have the following situation.

My Factory class needs to create appropriate Strategy objects based on the input string argument to the CreateStrategy function.

Strategy1, Strategy2 etc are all derived from a common StrategyBase class. However each strategy has a different Validation mechanism which is the type parameter to the Factory class. However, the StrategyValidators are not of any common type and have different interfaces.

Therefore, in the below code, I am unable to specify any common constraint on the StrategyValidator type.

I am new to C# and hence not sure if there exists any mechanism to get over this design issue. Please suggest

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

Here's the intended usage

Factory f = new Factory(); 

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

where WMIValidator and ABCDValidator are unrelated types, but the actual classes created by CreateStrategy function are related in a hierarchy e.g. having a common base StrategyBase

Here is a sample code to illustrate the issue

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

The error I get is

The type 'T' cannot be used as type parameter 'T' in the generic type or method 'TestCSharp.Product1'. There is no boxing conversion or type parameter conversion from 'T' to 'TestCSharp.S1'.

标签

评论

I don't really understand your scenario fully but as far as I can tell the factory pattern you're using would have to instantiate products using reflection. This is a little ugly because it doesn't give the consumer any hints about what strategy types can be used with a given product name.

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

I think that the answer in this case is, it depends on what you want Product and Strategy to do. What you seem to be trying to do is splitting your logic in two branches. Then you want to couple it again by using generics, but as you can notice, it won't work.

Consider a scenario, similar to yours above -- But where each class implementing IStrategy has one instead of two methods which does side effect (i.e. print a string). You use generics when the range of types allowed have something in common. In the case I just mentioned, both have a method returning void and accepting no parameters; so we can add a method to IStrategy, for instance:

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

Now, you also said that Strategy1 and Strategy2 have a different validation mechanism. However, it seems to me that you use them in the same method and context (and thus the same parameters and variables), so there must be something that makes them similar. Still, having defined IStrategy in the way we require, we can just use that as a constraint for Create<T>. So, Factory becomes:

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

But there's still one case. If you don't want Product1 to be called with S2 as a generic type, or Product2 to have S1 as its generic, then why using generics in the first place? You could easily couple the products with their relative strategies and also simplify the code remarkably.

In case I missed something (or the entire question) please leave a comment and I'll try to adapt my answer.

EDIT: since now you've redefined your example and used S1 and S2 as interfaces, I can see what you mean. A way would be defining multiple generic types and constraints for Factory.Create. Example:

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

It would be impossible otherwise, as you properly stated, because there's no common ancestor of S1 and S2 which can be accepted by your Product classes.

You can change the function to take StrategyValidator as type.

From

public static StrategyBase CreateStrategy<StrategyValidator>(String Type)

To

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

To answer you question, You cannot avoid conditional checks.

To simplify the code can move the different combinations ("Type1", "Type2" , etc) to either dictionary or to the configuration if you use Dependency Injection, and then can you reflection.

Example.

    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>

Have you considered overloading the Create<> function? I don't have VisualStudio handy right now, but would the following code work for your situation?

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

Additionally, you may wish to move your type constraints to a lower level. Consider writing an abstract base ProductBase class (that inherits from an IProductBase interface?) as follows:

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

This may help to alleviate some of your headaches.

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 在泛型类型参数上调用静态方法(Calling a static method on a generic type parameter)
    问题 我希望做这样的事情,但是在C#中它似乎是非法的: public Collection MethodThatFetchesSomething<T>() where T : SomeBaseClass { return T.StaticMethodOnSomeBaseClassThatReturnsCollection(); } 我收到一个编译时错误: “ T”是“类型参数”,在给定的上下文中无效。 给定泛型类型参数,如何在泛型类上调用静态方法? 给定约束,静态方法必须可用。 回答1 在这种情况下,您应该直接在约束类型上调用static方法。 C#(和CLR)不支持虚拟静态方法。 所以: T.StaticMethodOnSomeBaseClassThatReturnsCollection ...没有什么不同: SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection 通过通用类型参数是不需要的间接寻址,因此不受支持。 回答2 为了详细说明先前的答案,我认为反思与您在此想要的更加接近。 我可以给出1001个为什么您应该或不应该做某事的原因,我将按要求回答您的问题。 我认为您应该在通用参数的类型上调用GetMethod方法,然后从那里开始。 例如,对于一个函数: public void doSomething<T>(
  • C# Generic Interface and Factory Pattern
    I am trying to create a Generic interface where the parameter type of one of the methods is defined by the generic EDIT I've changed the question slightly after realising I have probably confused matters by specifying a type parameter in the Factory creation method. What I have is two types of API calls that I need to make to a 3rd party API. The first retrieves a record from the API using an Id that is an int. The second also retrieves a record from the API but the Id is a string (guid). I have a class for each record type (ClientEntity and InvoiceEntity) that both implement a Generic
  • C#泛型和多态性:矛盾之言?(C# Generics and polymorphism: an oxymoron?)
    问题 我只想确认我对C#中的泛型有什么了解。 在我工作过的几个代码库中都提到了这一点,在这些代码库中,使用了一个通用基类来创建类型安全的派生实例。 我所说的一个非常简单的例子, public class SomeClass<T> { public virtual void SomeMethod(){ } } public class DeriveFrom :SomeClass<string> { public override void SomeMethod() { base.SomeMethod(); } } 当我然后想要以多态方式使用派生实例时,就会出现问题。 public class ClientCode { public void DoSomethingClienty() { Factory factory = new Factory(); //Doesn't compile because SomeClass needs a type parameter! SomeClass someInstance = factory.Create(); someInstance.SomeMethod(); } } 看起来,一旦将泛型引入继承层次结构或接口中,就不能再以多态的方式使用该类家族,除非它本身是内部的。 真的吗? 回答1 据我所知,使用代码不需要泛型类的细节(即
  • 如何在Scala的通用方法中创建特征的实例?(How do I create an instance of a trait in a generic method in scala?)
    问题 我正在尝试使用此方法创建特征的实例 val inst = new Object with MyTrait 这很好用,但是我想将此创建内容移到生成器函数中。 object Creator { def create[T] : T = new Object with T } 我显然将需要清单来以某种方式解决类型擦除问题,但是在此之前,我遇到了两个问题: 即使具有隐式清单,Scala仍要求T为特征。 如何为create [T]添加限制,以使T为特征? 如果我选择使用Class.newInstance方法动态创建实例而不是使用“ new”,那么如何在“带有T的新对象”中指定“ with”? 是否可以在运行时动态创建新的具体混入类型? 回答1 您无法做到这一点(即使有清单)。 new Object with T代码涉及创建一个新的匿名类,该匿名类表示Object with T的组合。 要将其传递给您的create函数,您将必须在运行时生成此新类(使用新的字节码),并且Scala没有在运行时生成新类的功能。 一种策略可能是尝试将工厂方法的特殊功能转移到类的构造函数中,然后直接使用构造函数。 另一种可能的策略是创建转换函数(隐式或其他),以转换为您希望与此类一起使用的特征。 回答2 我不确定您提出这个问题的动机是什么,但是您可以考虑将T的工厂作为隐式参数传递。 这被称为使用类型类或即席多态性
  • 实例化Java中的泛型类(Instantiating a generic class in Java [duplicate])
    问题 这个问题已经在这里有了答案: 实例化Java中的泛型类型(12个答案) 11个月前关闭。 我知道Java的泛型要逊于.Net的泛型。 我有一个通用类Foo<T> ,我真的需要使用无参数构造函数在Foo实例化一个T 如何解决Java的局限性? 回答1 一种选择是传递Bar.class (或您感兴趣的任何类型-以任何方式指定适当的Class<T>引用)并将该值保留为字段: public class Test { public static void main(String[] args) throws IllegalAccessException, InstantiationException { Generic<Bar> x = new Generic<>(Bar.class); Bar y = x.buildOne(); } } public class Generic<T> { private Class<T> clazz; public Generic(Class<T> clazz) { this.clazz = clazz; } public T buildOne() throws InstantiationException, IllegalAccessException { return clazz.newInstance(); } } public class
  • C ++中的泛型工厂(Generic factory in C++ [duplicate])
    问题 这个问题已经在这里有了答案: 有没有一种方法可以从持有类名的字符串中实例化对象? (9个答案) 6年前关闭。 我正在开发一个游戏,并且正在尝试实现一种聪明的方法,即通过解析文本文件在C ++中创建npc对象。 当前,这是在工厂对象中进行硬编码的。 像这样: IActor * ActorFactory::create(string actortype, Room * r, string name, int hp) { if(actortype == "Troll") { return new Troll(r, name, hp); } if (actortype == "Dragon") { return new Dragon(r, name, hp); } // ... and so on throw "Can't recognize type '"+actortype+"'."; } 我认为这是一种非常丑陋的方式。 由于它(除其他事项外)违反了打开/关闭原则。 我受过Java的教育,在Java中,我会做类似让每个IActor在程序执行开始时向ActorFactory报告其类名和类类型的事情。 然后,工厂将关系存储在映射中,然后可以轻松查找哪些字符串映射到哪个对象,然后可以轻松实例化该字符串。 编辑:我也希望能够使用可变数量/类型的参数来调用构造函数。 用C ++怎么做?
  • C#泛型问题-在构造函数中使用参数更新泛型类型(C# generics problem - newing up the generic type with parameters in the constructor)
    问题 我正在尝试创建一个泛型类,其中的新类是泛型类型的实例。 如下: public class HomepageCarousel<T> : List<T> where T: IHomepageCarouselItem, new() { private List<T> GetInitialCarouselData() { List<T> carouselItems = new List<T>(); if (jewellerHomepages != null) { foreach (PageData pageData in jewellerHomepages) { T item = new T(pageData); // this line wont compile carouselItems.Add(item); } } return carouselItems; } } 但我收到以下错误: 创建变量类型的实例时无法提供参数 我发现以下与我需要的内容非常相关的问题:将参数传递给模板类型的C#通用new() 但是,由于我在Generic类中而不是外部方法中调用方法,因此无法使用Jared的建议答案,因此无法指定具体的类。 有没有解决的办法? 我已经根据另一个问题尝试了以下方法,但是由于我不知道要指定的T的具体类型,因此它不起作用。 正如从泛型类内部而不是外部调用的那样: public
  • 创建其构造函数需要参数的泛型类型的实例?(Create instance of generic type whose constructor requires a parameter?)
    问题 如果BaseFruit具有接受int weight的构造函数,我可以用这种通用方法实例化一块水果吗? public void AddFruit<T>()where T: BaseFruit{ BaseFruit fruit = new T(weight); /*new Apple(150);*/ fruit.Enlist(fruitManager); } 在注释后面添加了一个示例。 似乎只有给BaseFruit一个无参数的构造函数,然后通过成员变量填写所有内容,我才能做到这一点。 用我的真实代码(不是关于水果),这是不切实际的。 -更新- 因此似乎无法通过任何方式通过约束来解决。 从答案中可以找到三种候选解决方案: 工厂模式反射活化剂 我倾向于认为反射是最不干净的一种,但是我不能在其他两种之间做出选择。 回答1 另外一个简单的例子: return (T)Activator.CreateInstance(typeof(T), new object[] { weight }); 请注意,在T上使用new()约束只是使编译器在编译时检查公共的无参数构造函数,用于创建该类型的实际代码是Activator类。 您将需要确保自己与现有的特定构造函数有关,并且这种要求可能是代码异味(或者,您应该在c#的当前版本中尝试避免这种情况)。 回答2 您不能使用任何参数化的构造函数。 如果您有一个“
  • Generic factory parameters in Typescript
    This is the sample from the official Typescript documentation for a generic factory. In this sample the constructor does not take parameters. function create<T>(c: {new(): T; }): T { return new c(); } How could I rewrite this so that in addition to a type, the factory method accepts other parameters and passes them when it invokes the constructor of the class it is instantiating? So the return statement would look something like this: return c(p1, p2); Something that wasn't clear to me is that this {new(): T; } is effectively an interface, inasmuch as it defines the terms of assessment for
  • 为什么构造函数中带有可选参数的类不能满足new()泛型约束?(Why isn't the new() generic constraint satisfied by a class with optional parameters in the constructor?)
    问题 以下代码无法编译,并产生“窗口小部件必须是具有公共无参数构造函数的非抽象类型”错误。 我认为编译器具有所需的所有信息。 这是一个错误吗? 疏忽大意? 还是在某些情况下这是无效的? public class Factory<T> where T : new() { public T Build() { return new T(); } } public class Widget { public Widget(string name = "foo") { Name = name; } public string Name { get; set; } } public class Program { public static void Main() { var widget = new Widget(); // this is valid var factory = new Factory<Widget>(); // compiler error } } 回答1 虽然这在逻辑上应该起作用,但不幸的是,它没有起作用。 CLR仍将您的构造函数视为基于参数的构造函数。 请记住,尽管C#支持可选参数,但是这是在编译时在编译器级别完成的。 基础类型仍然只包含带有单个参数的构造函数。 就CLR而言,“默认参数”将转换为属性,如下所示: public Widget(([Optional
  • Java工厂模式与泛型(Java Factory Pattern With Generics)
    问题 我希望BallUserInterfaceFactory返回具有适当泛型类型的用户界面的实例。 我陷入下面的错误中的示例: 绑定不匹配:BallUserInterfaceFactory类型的通用方法getBaseballUserInterface(BASEBALL)不适用于参数(BALL)。 推断的类型BALL不能有效替代有界参数 public class BallUserInterfaceFactory { public static <BALL extends Ball> BallUserInterface<BALL> getUserInterface(BALL ball) { if(ball instanceof Baseball){ return getBaseballUserInterface(ball); } //Other ball types go here //Unable to create a UI for ball return null; } private static <BASEBALL extends Baseball> BaseballUserInterface<BASEBALL> getBaseballUserInterface(BASEBALL ball){ return new BaseballUserInterface<BASEBALL
  • 有没有一种方法可以从持有类名的字符串中实例化对象?(Is there a way to instantiate objects from a string holding their class name? )
    问题 我有一个文件:Base.h class Base; class DerivedA : public Base; class DerivedB : public Base; /*etc...*/ 和另一个文件:BaseFactory.h #include "Base.h" class BaseFactory { public: BaseFactory(const string &sClassName){msClassName = sClassName;}; Base * Create() { if(msClassName == "DerivedA") { return new DerivedA(); } else if(msClassName == "DerivedB") { return new DerivedB(); } else if(/*etc...*/) { /*etc...*/ } }; private: string msClassName; }; /*etc.*/ 有没有办法将该字符串转换为实际的类型(类),从而使BaseFactory不必知道所有可能的Derived类,并且每个类都具有if()? 我可以用这个字符串产生一个类吗? 我认为可以在C#中通过反射来完成。 C ++中有类似的东西吗? 回答1 不,没有,除非您自己进行映射。 C +
  • Java Factory Pattern With Generics
    I would like my BallUserInterfaceFactory to return an instance of a user interface that has the proper generic type. I am stuck in the example below getting the error: Bound mismatch: The generic method getBaseballUserInterface(BASEBALL) of type BallUserInterfaceFactory is not applicable for the arguments (BALL). The inferred type BALL is not a valid substitute for the bounded parameter public class BallUserInterfaceFactory { public static <BALL extends Ball> BallUserInterface<BALL> getUserInterface(BALL ball) { if(ball instanceof Baseball){ return getBaseballUserInterface(ball); } //Other
  • Passing class static factory as method parameter in Dart
    I'm currently attempting to abstract making different HTTP requests by using generics. I'm using json_serializale for generating fromJson() and toJson() methods. Here is a simple model file: import 'package:json_annotation/json_annotation.dart'; part 'article.g.dart'; @JsonSerializable() class Article { int id = 0; String title = ""; Article({this.id, this.title}); factory Article.fromJson(Map<String, dynamic> json) => _$ArticleFromJson(json); Map<String, dynamic> toJson() => _$ArticleToJson(this); } I have a generic class that needs to be passed the fromJson-method, see: typedef
  • F#学习日记3
    1.F#泛型 解析: // Explicitly generic function. let function-name<type-parameters> parameter-list = function-body // Explicitly generic method. [ static ] member object-identifer.method-name<type-parameters> parameter-list [ return-type ] = method-body // Explicitly generic class, record, interface, structure, // or discriminated union. type type-name<type-parameters> type-definition 2.F#委托 解析:F#委托类似函数指针,delegate声明确定委托可以引用的与delegate相同的签名的方法。 type delegate-typename = delegate of type1 -> type2 3.F#枚举 解析: type enum-name = | value1 = integer-literal1 | value2 = integer-literal2 ... 4.F#模式匹配 解析: match
  • 我可以有可变数量的通用参数吗?(Can I have a variable number of generic parameters?)
    问题 在我的项目中,我具有以下三个接口,这些接口由用于管理具有不同结构的各种业务对象的合并的类实现。 public interface IMerger<TSource, TDestination> { TDestination Merge(TSource source, TDestination destination); } public interface ITwoWayMerger<TSource1, TSource2, TDestination> { TDestination Merge(TSource1 source1, TSource2 source2, TDestination destination); } public interface IThreeWayMerger<TSource1, TSource2, TSource3, TDestination> { TDestination Merge(TSource1 source1, TSource2 source2, TSource3 source3, TDestination destination); } 这很好用,但是我宁愿有一个IMerger接口,它指定可变数量的TSource参数,如下所示(下面的示例使用params ;我知道这是无效的C#): public interface IMerger
  • C#:具有构造函数的泛型类型?(C#: Generic types that have a constructor?)
    问题 我有以下 C# 测试代码: class MyItem { MyItem( int a ) {} } class MyContainer< T > where T : MyItem, new() { public void CreateItem() { T oItem = new T( 10 ); } } Visual Studio 无法编译它,错误位于使用“new”的行: 'T': cannot provide arguments when creating an instance of a variable type 是否可以在 C# 中使用非无参数构造函数创建泛型类型的对象? 在 C++ 模板中做这样的事情没有问题,所以我很好奇为什么我不能在 C# 中做同样的事情。 也许需要一些额外的“位置”或语法不同? 回答1 它可以通过反射来完成: public void CreateItem() { int constructorparm1 = 10; T oItem = Activator.CreateInstance(typeof(T), constructorparm1) as T; } 但是没有通用约束来确保T实现所需的构造函数,因此我不建议这样做,除非您小心地在实现接口的每个类型中声明该构造函数。 回答2 C# 和 VB.Net
  • Java:通配符类型不匹配导致编译错误(Java: Wildcard Types Mismatch Results in Compilation Error)
    问题 我在项目中创建了一个工厂类,从理论上讲,该类允许我为任何(受支持的)给定类型创建管理器。 与管理者进行交互使我可以更改给定类型的某些属性。 我面临的问题是,当我尝试为泛型类型创建管理器时,编译器粉碎了我的希望和梦想。 以下代码是我正在使用的代码的简化版本。 我尝试创建“ test3Manager”的行将不会编译,并且我试图了解为什么会出现这种情况。 它下面的行显示了一种“解决方法”,我正试图避免这种情况。 import java.util.List; public class GenTest { public static void main(String[] args) { String test1 = ""; IRandomType<String> test2 = null; IAnotherRandomType<?> test3 = null; IManager<String> test1Manager = Factory.createManager(test1); IManager<IRandomType<String>> test2Manager = Factory.createManager(test2); IManager<IAnotherRandomType<?>> test3Manager = Factory.createManager(test3); //
  • 如何在Activator.CreateInstance中传递ctor args或使用IL?(How to pass ctor args in Activator.CreateInstance or use IL?)
    问题 我需要一个性能增强的Activator.CreateInstance(),并遇到了Miron Abramson的这篇文章,该文章使用工厂在IL中创建实例,然后对其进行缓存。 (以防万一消失,下面的代码来自Miron Abramson的网站)。 我是IL Emit代码的初学者,除了Activator.CreateInstance()之外的任何实例都可以实例化一个类,任何帮助都将是非常有价值的。 我的问题是,我需要创建一个带ctor参数的对象实例。 我看到有一种方法可以传递参数的Type,但也有一种方法可以传递ctor参数的值吗? 如果可能的话,我想使用类似于CreateObjectFactory<T>(params object[] constructorParams)因为我要实例化的某些对象可能具有多个ctor参数。 // Source: http://mironabramson.com/blog/post/2008/08/Fast-version-of-the-ActivatorCreateInstance-method-using-IL.aspx public static class FastObjectFactory { private static readonly Hashtable creatorCache = Hashtable.Synchronized
  • java parameterized generic static factory
    Is it possible in Java to create a static factory method/class that uses an interface as the parameterized type and return an implementing class of this given interface? Although my knowledge of Generics is limited, here is what I want to do: // define a base interface: public interface Tool { // nothing here, just the interface. } // define a parser tool: public interface Parser extends Tool { public ParseObject parse(InputStream is); } // define a converter tool: public interface Converter extends Tool { public ConvertObject convert(InputStream is, OutputStream os); } // define a factory