我有一个不同Line
- 类的继承树,从抽象Line
- 类开始。我希望能够将每一行与每一行相交,有时,我不知道两种运行时类型,例如我正在呼叫Line.Intersect(Line)
(所以我需要双重调度)。这将始终调用覆盖Intersect
- 方法的最抽象的重载,例如, Circle.Intersect(Line)
代替Circle.Intersect(actualType)
。这是一些示例代码:
class Program
{
static void Main(string[] args)
{
Line straightLine = new StraightLine();
Line circle = new Circle();
// Will print: "Circle intersecting a line."
// But should print: "Circle intersecting a straight line."
circle.Intersect(straightLine);
Console.ReadLine();
}
}
abstract class Line
{
public abstract void Intersect(Line line);
public abstract void Intersect(StraightLine straightLine);
public abstract void Intersect(Circle circle);
}
class StraightLine : Line
{
public override void Intersect(Line line)
{
Console.WriteLine("Straigth line intersecting a line.");
}
public override void Intersect(StraightLine straightLine)
{
Console.WriteLine("Straight line intersecting a straight line.");
}
public override void Intersect(Circle circle)
{
Console.WriteLine("Straight line intersecting a circle.");
}
}
class Circle : Line
{
public override void Intersect(Line line)
{
Console.WriteLine("Circle intersecting a line.");
}
public override void Intersect(Circle circle)
{
Console.WriteLine("Circle intersecting a circle.");
}
public override void Intersect(StraightLine straightLine)
{
Console.WriteLine("Circle intersecting a straight line.");
}
}
一种可能的解决方法是使用我目前所做的dynamic
。但是,我想迁移到.NET标准库,其中不允许dynamic
。
还有其他方法可以让这项工作成功吗?如果有帮助的话,我愿意为一个或多个接口切换抽象类。也许访客模式是适用的,虽然我只看到这用于不同的继承树(并且发现它非常难看)。
答案 0 :(得分:0)
可以通过使用反射来模拟双重调度。针对.NET Standard 1.1并使用Nuget-Package System.Reflection
,Intersect(Line line)
- 方法不需要是抽象的或虚拟的,但只需实现一次。
这是.NET标准库的完整示例代码(我现在返回string
而不是使用Console.WriteLine()
,因为后者在.NET Standard中不可用):
using System.Reflection;
namespace IntersectLibrary
{
public abstract class Line
{
public string Intersect(Line line)
{
var method = this.GetType().GetRuntimeMethod(nameof(Intersect), new[] { line.GetType() });
return (string)method.Invoke(this, new[] { line });
}
public abstract string Intersect(StraightLine straightLine);
public abstract string Intersect(Circle circle);
}
public class StraightLine : Circle
{
public override string Intersect(StraightLine straightLine)
{
return "Straight line intersecting a straight line.";
}
public override string Intersect(Circle circle)
{
return "Straight line intersecting a circle.";
}
}
public class Circle : Line
{
public override string Intersect(Circle circle)
{
return "Circle intersecting a circle.";
}
public override string Intersect(StraightLine straightLine)
{
return "Circle intersecting a straight line.";
}
}
}
请注意,在定位.NET Framework时,System.Reflection
提供了不同的方法,并且需要修改代码。
在控制台应用程序中,将发生以下情况:
using System;
using IntersectLibrary;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
Line straightLine = new StraightLine();
Line circle = new Circle();
Circle circle2 = new Circle();
// Calls "Line.Intersect(Line)", and correctly
// prints "Circle intersecting a straight line.".
Console.WriteLine(circle.Intersect(straightLine));
// Also calls "Line.Intersect(Line)",
// since the argument's compile-time type is "Line".
Console.WriteLine(circle2.Intersect(straightLine));
// Calls "Line.Intersect(Circle)",
// since the argument's compile-time type is "Circle".
// At runtime, the call will be resolved to
// "StraightLine.Intersect(Circle)" via single dispatch.
Console.WriteLine(straightLine.Intersect(circle2));
Console.ReadLine();
}
}
}
如果现在有一个编译时类型Line
的对象和一个具体类型的对象(例如Circle
),最好调用Line.Intersect(Circle)
,因为这不需要(慢)反射来解决方法调用。但是,Circle.Intersect(Line)
也可以使用,最重要的是,现在始终可以调用Line.Intersect(Line)
。