从通用方法返回对象作为接口

时间:2012-02-09 15:06:44

标签: c# design-patterns architecture interface factory-method

我有一个接口InterfaceBase和一些从它派生的接口Interface1, Interface2。接下来,我有一些实现InterfaceX接口的类,而不是基类。

现在,我是仿制药的初学者,这里的许多新方法让我头脑中变得非常混乱:(。我想创建工厂(静态类),我称之为

Interface1 concrete1 = Factory.Get<Interface1>();

这是我的(示例)工厂实现,不起作用:

  public static class Factory {

    public static T Get<T>() where T: InterfaceBase{

      Type type = typeof(T);

      //return new Concrete1() as T; // type T cannot be used with the as
      //return new Concrete1() as type; //type not found
      //return new Concrete1(); // cannot implicitly convert
      //return new Concrete1() as InterfaceBase; //cannot convert IBase to T
      //return new Concrete1() as Interface1; //cannot convert Interface1 to T
    }
  }

我想要实现的是从应用程序的其余部分隐藏类(它们是webservice处理程序)以轻松地交换它们。我想使用工厂,因为类将是单例,它们将存储在工厂内的Dictionary中,因此工厂可以通过此方法将它们分布在应用程序中,但作为接口.. 也许我没有正确使用约束 我做错了吗?我的方法不好吗?可以有更好的东西,也许整个建筑都要重新设计?图表更好地显示了architecture。工厂不在其中

5 个答案:

答案 0 :(得分:5)

您正在寻找的是“穷人依赖注入”。我想你应该使用一个真正的IoC容器,有很多选项(Unity,Castle Windsor,Ninject ......)。

但无论如何,如果你坚持自己做,那就去@Sergey Kudriavtsev推荐。只需确保为每个接口返回适当的具体类。像这样:

public interface InterfaceBase { }
public interface Interface1 : InterfaceBase { }
public interface InterfaceX : InterfaceBase { }

public class Concrete1 : Interface1 { }
public class ConcreteX : InterfaceX { }

public static class Factory
{
    public static T Get<T>()
        where T : InterfaceBase
    {
        if (typeof(Interface1).IsAssignableFrom(typeof(T)))
        {
            return (T)(InterfaceBase)new Concrete1();
        }
        // ...
        else if (typeof(InterfaceX).IsAssignableFrom(typeof(T)))
        {
            return (T)(InterfaceBase)new ConcreteX();
        }

        throw new ArgumentException("Invalid type " + typeof(T).Name, "T"); // Avoids "not all code paths return a value".
    }
}

通过将接口引用传递给工厂来调用它:

var instance = factory.Get<Interface1>();

答案 1 :(得分:2)

这应该有效:

return (T)(new Concrete1());

调用工厂方法的代码也应如下:

Interface1 concrete1 = Factory.Get<Interface1>();

答案 2 :(得分:2)

回答你问题的一部分:

return new Concrete1() as T; // type T cannot be used with the as 

T类型不能与as一起使用,因为T不知道是引用类型。您可以将类约束为具有where T : class [, ...]约束的引用类型。这将允许您使用as运算符,当然,假设您不需要将此方法与值类型一起使用。

修改

话虽如此,我更喜欢rsen娜的回答。由于您在编译时知道直接强制转换可以工作,因此使用直接强制转换比as更有意义。我也同意他建议您调查“真正的”IoC容器,但我会补充说,当你了解它们时,对泛型的强烈理解对你非常有用。既然你说你是泛型的初学者,像这样的练习可能是好主意,因为它可以帮助你学习泛型,并让你更好地欣赏IoC容器的价值可以添加。

编辑2

我看到另一个问题:你将T限制为InterfaceBase,然后将Concrete1转换为T.不知道Concrete1是从T派生的!这当然是你使用as演员的原因。所以答案是,添加class约束,你应该没问题。

编辑3

正如rsenna所指出的那样,您还可以通过向上转发和向下转发获得运行时类型检查:

return (T)(object)new Concrete1();

return (T)(InterfaceBase)new Concrete1();

我想知道这与效率如何比较

return new Concrete1() as T;

如果我找到一些时间,我今天晚些时候会检查。

答案 3 :(得分:1)

做这样的事

public static class Factory {

public static T Get<T>() where T: InterfaceBase{

  return (T) new Concrete1();
}

无法安全打字。您无法保证使用T == Concrete1调用该方法。 T可以是InterfaceBase的任何子类型,Concrete1只是其中一个子类型而不一定是相同的T,因此编译器不允许转换为T,就像它不允许你转换为字符串或任何一样其他不相关的类型。

Activator.CreateInstance()是一种处理方式:CreateInstance确保构建的实例是T类型,这是方法的预期输出值。

答案 4 :(得分:0)

如果您知道T是InterfaceBase的子类型(其中T:InterfaceBase),那么您可以将Get方法的返回类型设置为InterfaceBase:

public static InterfaceBase Get<T>() where T : InterfaceBase
{        
    return new Concrete1();
}

可以使用InterfaceBase的子接口作为T:

调用此方法
InterfaceBase ib = Factory.Get<Interface1>();

如果您传递除InterfaceBase的子接口之外的任何内容,则编译器会抱怨。

我认为这是一种更好的方法,但是@phoog指出它只适用于T类型参数的具体类:

public static T Get<T>() where T : InterfaceBase
{
    Type type = typeof(T);
    if (t.IsAbstract || t.IsInterface)
{
        throw new ArgumentException(@"Only non-abstract classes supported as T type parameter.");
}
    return Activator.CreateInstance<T>();
}