DoSomething(Car car);
DoSomething(Bike bike);
public class Car : Vehicle {}
public class Bike : Vehicle {}
public abstract class Vehicle {}
void run(Vehicle vehicle) {
DoSomething(vehicle);
}
这感觉就像一个简单的问题,但我遇到了问题。滴定(车辆(不存在,因此,即使车辆"保证"是汽车或自行车,dosomething(车辆(也会出错。我如何说服编译器"车辆"是自行车或汽车,以便可以运行滴定?
当然,我可以沿着
的另一种方法DoSomething(Vehicle vehicle)
{
if(vehicle is Car) ... etc
}
,但肯定有一种更干净的方法?
编辑/Clarity
将此代码放在经理类中,而不是在车辆类中存在dosomething((,这是每辆车都需要访问程序的不同部分。例如:
DoSomething(Car car) {
motorwayInfo.CheckMotorwayStatus();
}
DoSomething(Bike bike) {
cycleInfo.CheckCyclePathStatus();
}
不确定这个类比是否真的在我的特定问题上很好,哈哈 - 但基本上,我不希望汽车对Cycleinfo有任何参考,也不希望汽车对RoidwayWayInfo有任何参考。但是,将滴定物放入车辆上基本上意味着其参数需要:
DoSomething(CycleInfo cycleInfo, MotorwayInfo motorwayInfo)
或
DoSomething(InfoManager infoManager)
我都不知道每个子类型都只能使用特定的信息对象。我要这一切都错了吗?
这里的真正问题是 - 您期望发生什么方法的参数?如果是这样,则取决于参数的混凝土(sub(类型,那么该行为的适当位置是参数基类的虚拟(甚至是抽象(成员-Vehicle
。
class Vehicle
{
public abstract void Behave();
}
class Car : Vehicle
{
public override void Behave()
{
// Do something specific to a car
}
}
class Bike : Vehicle
{
public override void Behave()
{
// Do something specific to a bike
}
}
...
void Run(Vehicle vehicle)
{
vehicle.Behave();
}
您可以从此代码中看到,我恢复了角色。Run
函数是不负责知道具体参数应如何行为的负责人。取而代之的是,每个混凝土对象都通过Vehicle
参数必须知道如何表现。那是正确的多态性。
关于Run
方法,其有关参数的所有职责均应与所有参数对象的常见类型有关,这就是基类Vehicle
。在这方面,该方法可以访问基类定义的成员,或将对象插入集合等等。
void Run(Vehicle vehicle)
{
vehicle.Behave();
List<Vehicle> list = ...
list.Add(vehicle);
}
所需的内容称为double Dispatch,并且不受C#编译器的本地支持。
您可以通过一些重构来实现它:
public void DoSomething(Car car) {}
public void DoSomething(Bike bike) {}
public abstract class Vehicle
{
// ...
public abstract void CallDoSomething();
}
public class Car : Vehicle
{
public override void CallDoSomething()
{
DoSomething(this);
}
}
public class Bike : Vehicle
{
public override void CallDoSomething()
{
DoSomething(this);
}
}
或,如果您不关心性能惩罚,则可以使用dynamic
关键字将过载分辨率推迟到运行时,然后选择与类型匹配的最合适的方法:
void run(dynamic vehicle)
{
DoSomething(vehicle);
}
请参阅MSDN
车辆被"保证"为汽车或自行车
那不是真的。如果所有汽车都是车辆,并且所有自行车都是车辆,那并不意味着所有车辆都是自行车或汽车。子类型不起作用。
我该如何说服编译器"车辆"是自行车或汽车,以便可以运行吗?
做到这一点的唯一方法是铸造,您已经提到了:
if (vehicle is Car) (vehicle as Car).CheckMotorwayStatus();
else if (vehicle is Bike) (vehicle as Bike).CheckCyclePathStatus();
,但肯定有一种更干净的方法?
Zoran的答案建议的方法是更清洁的方法。
我不希望汽车对CycleInfo有任何参考,也不希望自行车提及RoidwayInfo。
有了一些想法,即使您的设置稍微更复杂,您也可以使用它。例如:
public abstract class Vehicle
{
public abstract void PrintRouteStatus();
}
public class MotorwayInfo
{
}
public class CycleInfo
{
}
public class Car : Vehicle
{
// probably pass this in via a constructor.
public MotorwayInfo _motorwayInfo = new MotorwayInfo();
public override void PrintRouteStatus()
{
Console.WriteLine(_motorwayInfo);
}
}
public class Bike : Vehicle
{
// probably pass this in via a constructor.
public CycleInfo _cycleInfo = new CycleInfo();
public override void PrintRouteStatus()
{
Console.WriteLine(_cycleInfo);
}
}
它可以在所有自行车的信息中都没有意识到,反之亦然。
public static void Main()
{
var p = new Program();
p.DoSomething(new Car());
p.DoSomething(new Bike());
}
public void DoSomething(Vehicle v)
{
v.PrintRouteStatus();
}
我感到惊讶的是,这些答案都不包括通用或接口。您可以扭转这一点并创建一个接受的实例。
public abstract class Vehicle {}
public class Car : Vehicle {}
public class Bike : Vehicle {}
public void DoSomething<T>(T _vehicle) where T : Vehicle
{
// _vehicle can be an instance of Car or Bike
if (_vehicle is Car _car)
motorwayInfo.CheckMotorwayStatus();
if (_vehicle is Bike _bike)
cycleInfo.CheckCyclePathStatus();
}
您可以进一步迈出一步并实现IVehicle
接口。
fwiw,我写了我创造的内容为" DoubleDisPatchObject",以帮助解决此类问题 - 值得注意的是,
1(不丢失类型安全性(或诉诸动态关键字的使用,无论是在呼叫站点还是卡利亚(,
2(在基本类的选择上没有负担(而是简单的组成模式就足够(,
和3(仅产生几行样板。
这是您的场景的样子:
public class VehicleManager
{
// boilerplate start
private DoubleDispatchObject dispatch;
public void DoSomething(Vehicle vehicle) =>
this.EnsureThreadSafe(ref dispatch)
.Via(nameof(DoSomething), vehicle, () => throw new NotImplementedException());
// boilerplate end
public void Run(Vehicle vehicle) =>
DoSomething(vehicle);
public void DoSomething(Car car) =>
Console.WriteLine("Doing something with a car...");
public void DoSomething(Bike bike) =>
Console.WriteLine("Doing something with a bike...");
}
public abstract class Vehicle { }
public class Car : Vehicle { }
public class Bike : Vehicle { }
class MainClass {
public static void Main (string[] args) {
var manager = new VehicleManager();
manager.Run(new Car());
manager.Run(new Bike());
Console.WriteLine("Done.");
}
}
在repl.it上:
https://repl.it/@ysharp_design/so48975551
另请参阅此相关的拉请请求(有关单位测试(:
https://github.com/waf/multipledispatchbenchs/pull/1/files
'hth