从泛型方法返回对象作为接口
本文关键字:接口 对象 泛型方法 返回 | 更新日期: 2023-09-27 18:24:06
我有一个接口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服务处理程序),以便轻松地交换它们。我想使用工厂,因为类将是singleton,它们将存储在工厂内的Dictionary中,所以工厂可以通过这种方法在应用程序中传播它们,但作为接口。。也许我没有正确使用约束我做错什么了吗?我的方法不好吗?能有更好的东西吗,也许整个建筑应该重新设计?图表可以更好地显示体系结构。工厂不在中
Methings您正在寻找的是一个"穷人依赖注入"。我想你应该使用一个真正的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>();
这应该有效:
return (T)(new Concrete1());
此外,调用工厂方法的代码应该是这样的:
Interface1 concrete1 = Factory.Get<Interface1>();
回答问题的一部分:
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和downstast进行运行时类型检查:
return (T)(object)new Concrete1();
或
return (T)(InterfaceBase)new Concrete1();
我想知道在效率方面与相比如何
return new Concrete1() as T;
如果我有时间的话,我今天晚些时候会检查。
执行类似的操作
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,这是该方法的预期输出值。
如果您知道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>();
}