一劳永逸地使用泛型实现工厂模式

coc*_*pup 4 c# oop generics inheritance

考虑一下。我想创建一个创造动物的工厂(模式,而不是计划一个新的起源)。我以为我会很聪明并创建一个类,其中包含我需要的 3 样东西才能工作,

  • 返回抽象动物的委托
  • 一种为每个动物返回特定动物的创建方法
  • 使用委托的每个创建方法的实例

厌倦了每次需要使用工厂模式时再次这样做并获得收益,我想我会更加聪明并一劳永逸地解决它。所以,我创建了这个漂亮的类

class Factorable<T> where T: class, new() 
{
    delegate T CreateDelegate();
    static CreateDelegate DoCreate = new CreateDelegate (CreateSelf);
    static T CreateSelf()
    {
        return new T();
    }
}

class Factory<T> where T : Factorable<T>
{
    public Factorable<T>.CreateDelegate CreationMethod ;
} 
Run Code Online (Sandbox Code Playgroud)

我想,很酷,我可以让顶级类(动物)从这个类继承,这样我就不必为所有动物编写和实例化所有特定的创建方法。由于泛型,这一切都将完成。几乎......看到这个:

class Animal:Factorable<Animal> {...}
class Bird:Animal {...}

Factory genesis = new Factory<Animal>();
genesis.CreationMethod = Animal.DoCreate;
Animal instance = genesis.CreateAnimal();  //instance is a brand new abstract Animal

genesis.CreationMethod = Bird.DoCreate;  //lets make it create birds!
instance = genesis.CreateAnimal();  // wrong, instance is still an abstract Animal
Run Code Online (Sandbox Code Playgroud)

有没有什么办法解决这一问题?我想要 Bird 继承的 CreateSelf 方法来创建 Birds,而不是抽象的 Animals(无需为 Bird 编写新方法)。有没有办法指定 Animal 继承自 Factorable 但让它的后代用它自己的类型覆盖泛型 T ?

像这样的东西(这是愚蠢的代码,不起作用)

class Animal:Factorable<Animal... or better the actual type of the class that has inherited>
Run Code Online (Sandbox Code Playgroud)

Aoi*_*asu 5

你是不是有点过于复杂了?假设Animal是你的基类:

public class Factory
{
    public static T Create<T>() where T : Animal, new()
    {
        return new T();
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

var a = Factory.Create<Animal>();
var b = Factory.Create<Bird>();
Run Code Online (Sandbox Code Playgroud)

更新

阅读您的评论后,这就是我的理解:调用工厂的对象不知道所创建实例的确切类型。它只知道它是 Animal 类或 Animal 派生类。那么,这个怎么样:

public class Factory
{
    private Type _outputType = typeof(Animal);

    public void Produces<T>() where T : Animal, new()
    {
        _outputType = typeof(T);
    }

    public Animal CreateAnimal()
    {
        return (Animal)Activator.CreateInstance(_outputType);
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:将输出类型Produces<T>设为私有并使用来设置它提供了确保输出类型为 Animal 或派生类型的简便方法。

用法:

var f = new Factory();  // factory produces animals
var a = f.CreateAnimal();
f.Produces<Bird>();     // from now on factory produces birds
var b = f.CreateAnimal();
Run Code Online (Sandbox Code Playgroud)