我有一个接口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。工厂不在其中
答案 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娜的回答。由于您在编译时知道直接强制转换可以工作,因此使用直接强制转换比我也同意他建议您调查“真正的”IoC容器,但我会补充说,当你了解它们时,对泛型的强烈理解对你非常有用。既然你说你是泛型的初学者,像这样的练习可能是好主意,因为它可以帮助你学习泛型,并让你更好地欣赏IoC容器的价值可以添加。as
更有意义。
编辑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>();
}