如何使用接口在C#中剥离泛型?

时间:2012-11-05 18:33:01

标签: c# generics inheritance interface factories

我目前有一个继承结构,其中方法的返回类型被保留为开放泛型,以便每个类可以从另一个结构的相应级别返回一个对象。例如,假设我们有一个VehicleFactory,其中TVehicle:可以生产车辆TVehicle的车辆。我也有CarFactory:VehicleFactory,TVehicle:Car。

汽车继承自车辆,所以这一切都是有效的,让我知道我的CarFactory必须生产汽车,而我的汽车工厂可以生产任何类型的汽车。我遇到的问题是,我需要一种方法将VehicleFactory实例化为CarFactory,当它由Ford运行时,但作为一个BoatFactory由Wave Runner运行时。

我想我可以通过创建一个与VehicleFactory的功能相匹配的接口并编写一个返回IVehicleFactory的MakeFactory方法来做到这一点(这将非通用地返回车辆)。由于CarFactory返回汽车,它是车辆,它实现了界面,所有都应该是正确的。意外的问题是,尽管TVehicle必须是车辆,但VehicleFactory无法满足作为车辆TVehicle关闭的界面。

有谁知道为什么会这样或者是否有另一种方法来解决这个限制?如果没有直接解决此限制的方法,是否有任何替代方法来拥有共享功能集,确保它始终被实例化为两组或更多组更具体的类之一。 (其中Vehicle是共享层,Car和Boat是特定于上下文的层。)

class Vehicle
{
}

class Car : Vehicle
{
}

interface IVehicleFactory
{
     Vehicle ProduceVehicle();
}

class VehicleFactory<TVehicle> : IVehicleFactory
    where TVehicle:Vehicle
{
    public virtual TVehicle ProduceVehicle()
    {
    }
}

class CarFactory<TVehicle> : VehicleFactory<TVehicle>
    where TVehicle : Car
{
    public override TVehicle ProduceVehicle()
    {
    }
}

使用它的代码

static IVehicleFactory CreateVehicleFactory()
{
    if(somecondition)
    {
         Return new CarFactory<Car>();
    }
    else
    {
         Return new BoatFactory<Boat>();
    }
}

添加更多细节以澄清问题。

术语工厂的使用并不意味着工厂模式。它实际上是一个检索“车辆”的存储库。车辆类型是具有公共共享基本代码的库的应用程序特定版本。每个应用程序Car和Boat的存储库对于相同的检索可能具有不同的逻辑,并且可能依赖于其对象的Car或Boat变体中的字段。我需要一种方法来构建一个工厂,它可以查看我所在应用程序的上下文并返回相应的存储库(BoatFactory或CarFactory),这样车辆级别的共享代码将正确使用任何特定于应用程序的重写功能。 / p>

4 个答案:

答案 0 :(得分:2)

希望这有帮助:

interface IVehicleFactory<out T> where T : Vehicle
{
    T Create();
}

class CarFactory : IVehicleFactory<Car>
{
    public Car Create()
    {
        return new Car();
    }
}

class VehicleFactoryProvider
{
    public IVehicleFactory<T> GetFactoryFor<T>() where T : Vehicle
    {
        throw new NotImplementedException();
    }
}

class Program
{
    public static void Main(string[] args)
    {
        VehicleFactoryProvider provider = new VehicleFactoryProvider();
        var factory = provider.GetFactoryFor<Car>();
        var car = factory.Create();
    }
}

答案 1 :(得分:1)

您的VehicleFactory<TVehicle>不满足IVehicleFactory界面。为了满足它,它必须有一个带有签名Vehicle ProduceVehicle()的方法。你显然没有一个带有该签名的方法(但返回一个派生类型),所以它不起作用。

处理此问题的最简单方法是显式实现接口并返回调用其他方法的结果。界面将得到满足,您仍在生产正确类型的车辆。这类似于在实现IEnumerable<T>接口时通常要执行的操作(需要实现IEnumerable接口)。

class VehicleFactory<TVehicle> : IVehicleFactory
    where TVehicle:Vehicle
{
    public virtual TVehicle ProduceVehicle()
    {
        throw new NotImplementedException();
    }

    // explicitly implement the interface by
    // returning the value of our actual method
    Vehicle IVehicleFactory.ProduceVehicle()
    {
        return ProduceVehicle();
    }
}

答案 2 :(得分:0)

我在想,也许这里可能有一个小型的建筑重构;在语义上,似乎你只需要:

public class VehicleFactory
{
    protected Dictionary<Type, ISpecificTransportFactory> _mapping = // initialize

    public Vehicle CreateVehicle<TVehicle>() // any params
    {
        if(_mapping.ContainsKey(typeof(TVehicle))
          return _mapping[typeof(TVehicle)].CreateVehicle(); // any params that need to be passed

        throw new ConfigurationInvalidException("No transportFactory defined for : " + typeof(TVehicle).Name);
    }
}

然后简要介绍一下:

public interface ISpecificTransportFactory
{
     Vehicle CreateVehicle();
}

public abstract class FactoryBase<TVehicleType> : ISpecificTransportFactory
    where TVehicleType : Vehicle
{
    public Vehicle CreateVehicle()
    {
        return CreateInstance();
    }

    public abstract TVehicleType CreateInstance();
}

public class CarFactory : FactoryBase<Car>
{
     public override Car CreateInstance()
     {
          return new Car();
     }
}

理论上,您可以按以下方式使用它:

public static void Main(string[] args)
{
     var vehicleFactory = new VehicleFactory();

     var vehicle = vehicleFactory.CreateVehicle<Car>();
}

答案 3 :(得分:0)

好吧,虽然我还没有解决方法如何使其工作,(至少没有完整),我想我现在明白了这个问题。我最初的假设之一似乎是不正确的。我的印象是,界面上不太具体的返回类型可以通过更具体的类型来实现。即,定义SomeBaseClass作为方法的返回类型的接口不能由实现返回SomeChildClass的方法的类实现,其中SomeChildClass继承自SomeBaseClass。

使用协变泛型(通过out关键字)确实提供了某种方式来通过允许一般地定义接口来规避这一点,但是对于我的目的,当我点击需要基于IList的结果时,这会分崩离析(需要一个不变的类型参数)。

最终,可以做到的最好的是,我可以做到这一点,以便CarFactory只知道它正在使用车辆(通过移除TVehicle泛型参数上的附加where条件),需要在CarFactory中进行转换但是,当CarCompany实例化CarFactory时,它仍然可以将其作为CarFactory,而客户端代码本身也不必担心转换。

这并不理想,但我认为由于C#缺乏协变返回类型,它可能是最好的。

interface IVehicleFactory<TVehicle> where TVehicle:Vehicle
{
    TVehicle SomeMethod();
}

VehicleFactory<TVehicle> : IVehilceFactory<TVehicle> where TVehicle:Vehicle
{
    TVehicle SomeMethod()
    {
    }
}

CarFactory<TVehicle> : IVehicleFactory<TVehicle> where TVehicle:Vehicle
{
    TVehicle SomeMethod()
    {
       ~~Always returns car and uses cast from Vehicle when necessary.
    }
}

class VehicleFactory<TVehicle>
{
   static IVehicleFactory<TVehicle> CreateContextSpecificFactory()
   {
       if(someCondition)
       {
            return new CarFactory<TVehicle>;
       }
   }
}

class CarCompany
{
    CarFactory<Car> carFactory = new CarFactory<Car>;
    Car car = carFactory.SomeMethod();
}

class VehicleCompany
{
    VehicleFactory<Vehicle> vehicleFactory = VehicleFactory<Vehicle>.CreateContextSpecificFactory();
}