2

我有一个接口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
    }
  }

我想要实现的是从应用程序的其余部分隐藏类(它们是 Web 服务处理程序)以轻松交换它们。我想使用工厂,因为类将是单例,它们将存储在工厂内的字典中,因此工厂可以通过这种方法将它们传播到应用程序中,但作为接口..也许我没有正确使用约束是我做错事了?我的方法不好吗?有没有更好的东西,也许整个架构应该重新设计?图表以更好地显示架构。工厂不在里面

4

5 回答 5

7

我认为您正在寻找的是“穷人依赖注入”。我想您应该为此使用真正的 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>();
于 2012-02-09T15:22:01.223 回答
2

这应该有效:

return (T)(new Concrete1());

调用工厂方法的代码也应该是这样的:

Interface1 concrete1 = Factory.Get<Interface1>();
于 2012-02-09T15:10:23.393 回答
2

要回答您问题的一部分:

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

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

编辑

话虽如此,我更喜欢rsenna的回答。由于您在编译时就知道直接强制转换会起作用,因此使用直接强制转换比使用as. 我也同意他的建议,即你研究“真正的”IoC 容器,但我要补充一点,当你了解泛型时,对泛型的深刻理解对你非常有用。既然您说您是泛型的初学者,那么像这样的练习可能是一个好主意,因为它将帮助您了解泛型,并让您更好地了解 IoC 容器可以增加的价值。

编辑 2

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

编辑 3

正如 rsenna 指出的那样,您还可以使用 upcast 和 downcast 进行运行时类型检查:

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

或者

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

我想知道这在效率方面如何与

return new Concrete1() as T;

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

于 2012-02-09T15:25:07.657 回答
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 类型,这是该方法的预期输出值。

于 2012-02-09T15:54:07.807 回答
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 的子接口以外的任何内容作为 T 传递,编译器会报错。

我认为这是一种更好的方法,但正如@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>();
}
于 2012-02-09T15:21:21.773 回答