从泛型方法返回对象作为接口

本文关键字:接口 对象 泛型方法 返回 | 更新日期: 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>();
}