接口方法的force参数是每个类实现的具体类型

时间:2018-05-04 22:58:53

标签: c#

我正面临一个场景,我需要创建N个对象的实例(实现一个接口)并从中调用一个方法,该方法的参数可以在实现接口的每个类中有所不同,它是这样的:

//Definition
class BaseOutput
{
    public string Result {get; set;}
}

class BaseParam
{
    public string Name {get; set;}
}

class CarParam : BaseParam
{
    public string Wheels {get; set;}
}

class AirPlaneParam : BaseParam
{
    public string Engines {get; set;}
}

interface Vehicle
{
    IEnumerable<BaseOutput> Run(IEnumerable<BaseParam> parameters,
                                object anotherVal);
}

//Implementation
class Car : Vehicle
{
    //Here the parameters type must be restricted to be only of type IEnumerable<CarParam> 
    public IEnumerable<BaseOutput> Run(IEnumerable<BaseParam> parameters, object anotherVal)
    {
        //Do something specific to the Car
    }
}

class AirPlane : Vehicle
{
    //Here the parameters type must be restricted to be only of type IEnumerable<AirPlaneParam> 
    public IEnumerable<BaseOutput> Run(IEnumerable<BaseParam> parameters, object anotherVal)
    {
        //Do something specific to the AirPlane
    }
}

需要这种限制来防止使用每个类的特定属性的具体问题。

我将非常感谢您的帮助

2 个答案:

答案 0 :(得分:1)

使用两个原型

我的建议是为Run()使用两个方法原型,根据调用者的行为暴露不同的原型。这样,您就可以提供类型安全版和通用版Run()

<强>解释

问题是你有两个相互矛盾的要求:

  1. 您希望能够对所有车辆进行相同处理,即将它们存储在List<Vehicle>中,并能够遍历它们并在每个车辆上调用Run()方法。

  2. 您希望每种车型只允许某些类型的参数,即您希望每种类型的车辆都有不同的编译时规则。因此,您实际上希望能够遍历它们并在每个上运行Run();实际上你特别想要一个编译时错误。

  3. 这听起来很矛盾,但你可以做一些神奇的事情来做一些有点合情合理的事情。

    解决方案详情

    我建议的解决方案是两次实施Run()

    1. 在班级的默认界面中实施Run()的特定版本

    2. 在Vehicle界面中显式实现Run()的通用版本。如果您不知道这意味着什么,请参阅此问题:What's the difference between implementing an Interface explicitly or implicitly?

    3. 从后者调用前者,根据需要过滤输入(例如使用OfType())。

    4. 例如:

      class AirPlane : Vehicle
      {
          IEnumerable<BaseOutput> Vehicle.Run(IEnumerable<BaseParam> parameters, object anotherVal)
          {
              return Run(parameters.OfType<AirPlaneParam>(), anotherVal);
          }
          public IEnumerable<BaseOutput> Run(IEnumerable<AirPlaneParam> parameters, object anotherVal)
          {
              //Your implementation
          }
      }
      

      现在你可以吃蛋糕了:

      var baseParameters = new List<BaseParam>();
      var airPlaneParameters = new List<AirPlaneParam>();
      var vehicles = new List<Vehicle>();
      
      foreach (Vehicle vehicle in vehicles)
      {
          vehicle.Run(baseParameters, "Foo");      //Works, although only airplane parameters will get processed
          vehicle.Run(airPlaneParameters, "Foo");  //Works, due to generic covariance
      }
      
      foreach (AirPlane airplane in vehicles.OfType<AirPlane>())
      {
          airplane.Run(baseParameters, "Foo"); //Does not compile
          airplane.Run(airPlaneParameters, "Foo"); //Works
      }
      

      这是指向working code on DotNetFiddle的链接。

答案 1 :(得分:0)

您可以将参数类型设为 generic

interface Vehicle<in TParam> where TParam : BaseParam
{
    IEnumerable<BaseOutput> Run(IEnumerable<TParam> parameters,
                                object anotherVal);
}

//Implementation
class Car : Vehicle<CarParam>
{
    public IEnumerable<BaseOutput> Run(IEnumerable<CarParam> parameters, object anotherVal)
    {
        //Do something specific to the Car
    }
}

class AirPlane : Vehicle<AirPlaneParam>
{
    public IEnumerable<BaseOutput> Run(IEnumerable<AirPlaneParam> parameters, object anotherVal)
    {
        //Do something specific to the AirPlane
    }
}

这将限制您可以传递的内容:

new Car().Run(new CarParam[0], new object()); // allowed
new Car().Run(new BaseParam[0], new object()); // compile-time error
new Car().Run(new AirPlaneParam[0], new object()); // compile-time error

如果您在不知道其通用类型的情况下需要代表一堆车辆,那么您会发现困难:

var vehicles = new List<Vehicle<BaseParam>>();
vehicles.Add(new Car()); // compile-time exception.