仿制药和工厂

时间:2009-10-20 13:04:41

标签: c# generics

我是仿制药的新手,并且一直在试图弄清楚如何从工厂返回一个基类是通用类的实例。请参阅下面的示例代码这些问题在工厂类中突出显示:

public abstract class MyGenericBaseClass<T>
{
    public string Foo()
    {...}
}

public sealed class MyDerivedIntClass : MyGenericBaseClass<int>
{

}

public sealed class MyDerivedStringClass : MyGenericBaseClass<string>
{

}

public static class MyClassFactory
{
    public static MyGenericBaseClass<T> CreateMyClass<T>()
    {
        // **********************************************
        if (typeof(T) == typeof(int))
        {
            return new MyDerivedIntClass();
        }

        if (typeof(T) == typeof(string))
        {
            return new MyDerivedStringClass();
        }
        // **********************************************
    }
}

我如何解决这个问题?

提前感谢

Ohgee

7 个答案:

答案 0 :(得分:7)

在我的大多数泛型类中,我都放了一个非泛型接口。实际上,我会实现这样的事情:

public interface INonGenericInterface
{
    string Foo();
}

public abstract class MyGenericBaseClass<T> : INonGenericInterface
{
    public string Foo()
    {...}
}

public static class MyClassFactory
{
    public static INonGenericInterface CreateMyDerivedIntClass()
    {
        return new MyDerivedIntClass();
    }

    public static INonGenericInterface CreateMyDerivedStringClass()
    {
        return new MyDerivedStringClass();
    }
}

这样,您可以清楚地说明可以创建哪些类型,并且仍然可以将调用者与具体类型分离。

当然,对于这种情况,您不一定需要非通用接口,但实际上,您很可能需要它。

答案 1 :(得分:3)

首先,不清楚为什么要创建派生自泛型类的类,而不是直接使用泛型类。也不清楚为什么你需要一个工厂而不是直接实例化你需要的任何类。这些模式非常重要且有用,通常是解决某些问题的唯一方法,但它们不是要求。你应该有充分的理由来实现它们。

但是,我会假设你确实有充分的理由说你为了简洁而省略了。

使用工厂模式的目的是让一些代码实例化正确的类,而不知道正确的类。请注意,在您的示例中缺少此分隔:正在调用MyClassFactory.CreateMyClass<T> () 的人必须知道正确的类,因为该代码必须将该类型作为通用参数传递,这决定了正确的类。如果我足够了解CreateMyClass<int> (),那么我就足够了解new MyDerivedIntClass ()

实现工厂模式有两种主要方式:静态函数和工厂类。

使用这两种方法,您需要在泛型下面“抽象”基类或接口:

public interface IMyInterface
{
    string Foo ();
}

public abstract class MyGenericBaseClass<T> : IMyInterface
{
    // ...
    abstract /* or virtual */ string Foo ();
}

使用静态函数,您需要在某处定义委托类型(省略类范围):

// note: I'm not sure this is the correct syntax, but I think I'm in the ballpark
delegate IMyInterface MyInterfaceFactory ();

并且类可以实现它们以返回正确的类型。

public sealed class MyDerivedIntClass : MyGenericBaseClass<int>
{
    // ...
    static IMyInterface CreateObject () { return new MyDerivedIntClass (); }
}

public sealed class MyDerivedStringClass : MyGenericBaseClass<string>
{
    // ...
    static IMyInterface CreateObject () { return new MyDerivedStringClass (); }
}

将静态函数传递给实例化对象的函数:

// ... somewhere else in the code ...

// create a IMyInterface object using a factory method and do something with it
void Bar (MyInterfaceFactory factory)
{
    IMyInterface mySomething = factory ();
    string foo = mySomething.Foo ();
}

// ... somewhere else in the code ...
void FooBarAnInt ()
{
    Bar (MyDerivedIntClass.CreateObject);
}

第二种方法是使用工厂类:

public interface IMyInterfaceFactory
{
    IMyInterface CreateObject ();
}

public class MyDerivedIntFactory : IMyInterfaceFactory
{
    public IMyInterface CreateObject () { return new MyDerivedIntClass (); }
}

public class MyDerivedStringFactory : IMyInterfaceFactory
{
    public IMyInterface CreateObject () { return new MyDerivedStringClass (); }
}

// ... somewhere else ...

// create a IMyInterface object using a factory class and do something with it
void Bar (IMyInterfaceFactory factory)
{
    IMyInterface mySomething = factory.CreateObject ();
    string foo = mySomething.Foo ();
}

// ... somewhere else in the code ...
void FooBarAnInt ()
{
    Bar (new MyDerivedIntFactory ());
}

请注意,您可以(也可能应该)制作工厂类单例,但这是一个不同的问题。此外,您可以使用抽象基类而不是接口;您需要根据需要添加abstract(或virtual)和override

从根本上说,有人在某处必须知道要实例化的正确对象类型。工厂对象的要点不是完全抽象出那些知识,而是从实际创建对象的代码中分离知道要创建什么类型的对象。如果您不需要这种分离,则工厂模式不是特别有用。

答案 2 :(得分:2)

我通常使用Dictionary&lt; string,Type&gt;对于我的每个工厂,我在其中注册工厂可以返回的每个可用实现类型(嗯..请参阅此信息以获取更多信息:Is a switch statement applicable in a factory method?

您可以根据自己的需要进行调整,并保持字典&lt; Type,Type&gt;所有派生类的。

 ...
 Factories.StaticDictionary.Add(typeof(int),typeof(MyDerivedIntClass));
 Factories.StaticDictionary.Add(typeof(string),typeof(MyDerivedStringClass));
 ...

(或让你的每个派生类自动添加到这个字典中)

那么你的工厂就是:

    public static class MyClassFactory
    {
        public static MyGenericBaseClass<T> CreateMyClass<T>()
        {
          Activator.CreateInstance(
          Factories.StaticDictionary[typeof(T)]);
        }

    }

答案 3 :(得分:0)

如果你不想进行类型检查并实例化相应的类,那么我能想到的另一种方式就是反射,它可以在有多个类时为你节省一些行但实现起来会更复杂

老实说,我认为按照现在的方式进行操作是一个完全有效的工厂实现,并且无法真正理解为什么要更改它。

答案 4 :(得分:0)

我认为这不一定是坏事。在某些时候,您需要声明您正在创建的对象的类型。通过隐藏对各种派生类的构造函数的调用,工厂允许您在一个具有已知具体类的位置。

您可以通过多种方式执行此操作 - 一组if语句,一个字典,一个企业级xml配置文件 - 但最终您需要某种方法将int的类型参数映射到MyDerivedIntClass的新实例。它可能不是很漂亮,但至少你只需要编写一次代码。

答案 5 :(得分:0)

如果你想要比其他人发布的一些例子更进一步,你应该看一下依赖注入框架。

答案 6 :(得分:0)

感谢所有回复。在这里获得了巨大的教育。

这条路线的主要原因是:我有一个带有'ID'属性的基类,其数据类型是在运行时确定的(string,int或long是处理的主要类型)。工厂类/方法是返回派生类,其中包含派生类ID的approprite数据类型。这就是为什么我认为我不能选择具有非通用基类/接口的原因。

在30分钟之前发现了这个想法,它在工厂方法中做到了这一点:

public static class MyClassFactory
{    
    public static MyGenericBaseClass<T> CreateMyClass<T>()    
    {        
        // **********************************************        
        if (typeof(T) == typeof(int))        
        {
            MyGenericBaseClass<int> typedDerived = new MyDerivedIntClass();
            return (MyGenericBaseClass<T>)(object)typedDerived;      
        }        

        if (typeof(T) == typeof(string))        
        {            
            MyGenericBaseClass<string> typedDerived = new MyDerivedStringClass();
            return (MyGenericBaseClass<T>)(object)typedDerived;         
        }        
        // **********************************************    
    }
}

看起来很讨厌,尤其是双重演员,但似乎有效。你们在可维护性方面有什么看法?