我正面临一个场景,我需要创建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
}
}
需要这种限制来防止使用每个类的特定属性的具体问题。
我将非常感谢您的帮助
答案 0 :(得分:1)
我的建议是为Run()
使用两个方法原型,根据调用者的行为暴露不同的原型。这样,您就可以提供类型安全版和通用版Run()
。
<强>解释强>
问题是你有两个相互矛盾的要求:
您希望能够对所有车辆进行相同处理,即将它们存储在List<Vehicle>
中,并能够遍历它们并在每个车辆上调用Run()
方法。
您希望每种车型只允许某些类型的参数,即您希望每种类型的车辆都有不同的编译时规则。因此,您实际上不希望能够遍历它们并在每个上运行Run()
;实际上你特别想要一个编译时错误。
这听起来很矛盾,但你可以做一些神奇的事情来做一些有点合情合理的事情。
解决方案详情
我建议的解决方案是两次实施Run()
:
在班级的默认界面中实施Run()
的特定版本
在Vehicle界面中显式实现Run()
的通用版本。如果您不知道这意味着什么,请参阅此问题:What's the difference between implementing an Interface explicitly or implicitly?。
从后者调用前者,根据需要过滤输入(例如使用OfType())。
例如:
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.