2

我有以下形状层次结构:

public abstract class Shape
{ ... }

public class Rectangle : Shape
{ ... }

public class Circle : Shape
{ ... }

public class Triangle : Shape
{ ... }

我已经实现了以下功能来确定两个形状是否相交。我使用以下IsOverlapping扩展方法,它用于在运行时dynamic调用适当的重载IsOverlappingSpecialisation方法。我相信这被称为双重调度。

static class ShapeActions
{
    public static bool IsOverlapping(this Shape shape1, Shape shape2)
    {
        return IsOverlappingSpecialisation(shape1 as dynamic, shape2 as dynamic);
    }

    private static bool IsOverlappingSpecialisation(Rectangle rect, Circle circle)
    {
        // Do specialised geometry
        return true;
    }

    private static bool IsOverlappingSpecialisation(Rectangle rect, Triangle triangle)
    {
        // Do specialised geometry
        return true;
    }

这意味着我可以执行以下操作:

Shape rect = new Rectangle();
Shape circle = new Circle();

bool isOverlap = rect.IsOverlapping(circle);

我现在面临的问题是,我还必须实施以下ShapeActions工作circle.IsOverlapping(rect)

private static bool IsOverlappingSpecialisation(Circle circle, Rectangle rect)
{
    // The same geometry maths is used here
    return IsOverlappingSpecialisation(rect, circle); 
}

这是多余的(因为我需要为每个创建的新形状执行此操作)。有没有办法解决这个问题?我想过将Tuple参数传递给IsOverlapping,但我仍然有问题。本质上,我希望基于唯一的无序参数集发生重载(我知道这是不可能的,所以寻找解决方法)。

4

1 回答 1

3

我可能在这里使事情过于复杂,但它确实有效......

public static class OverlapCalculator
{
    private static readonly Dictionary<Tuple<Type, Type>, Delegate> Calculations = new Dictionary<Tuple<Type, Type>, Delegate>();

    public static bool IsOverlapping<TShape, TOtherShape>(this TShape shape, TOtherShape otherShape)
        where TShape : Shape
        where TOtherShape : Shape
    {
        var calculation = GetCalculationDelegate<TShape, TOtherShape>();
        if (calculation != null)
        {
            return calculation(shape, otherShape);
        }

        throw new InvalidOperationException(string.Format("Could not find calculation for {0} and {1}", typeof(TShape).Name, typeof(TOtherShape).Name));
    }

    public static void AddCalculation<TShape, TOtherShape>(Func<TShape, TOtherShape, bool> calculation)
        where TShape : Shape
        where TOtherShape : Shape
    {
        var key = new Tuple<Type, Type>(typeof(TShape), typeof(TOtherShape));
        Calculations[key] = calculation;

        var reverseKey = new Tuple<Type, Type>(typeof(TOtherShape), typeof(TShape));
        var reverseCalculation = new Func<TOtherShape, TShape, bool>((otherShape, shape) => calculation(shape, otherShape));
        Calculations[reverseKey] = reverseCalculation;
    }

    private static Func<TShape, TOtherShape, bool> GetCalculationDelegate<TShape, TOtherShape>()
    {
        var key = new Tuple<Type, Type>(typeof(TShape), typeof(TOtherShape));

        Delegate calculationDelegate;
        if (Calculations.TryGetValue(key, out calculationDelegate))
        {
            return (Func<TShape, TOtherShape, bool>) calculationDelegate;
        }

        return null;
    }
}

这只是将代表存储在 a 中,并在您调用aDictionary时尝试获取匹配的代表。IsOverlappingShape

你像这样使用它:

public class Program
{
    public static void Main()
    {
        // Add the calculation algorithm defined below.
        OverlapCalculator.AddCalculation<Rectangle, Triangle>(IsOverlapping);

        var rect = new Rectangle();
        var triangle = new Triangle();
        var circle = new Circle();

        // These will work since we have a two way calculation for Rectangle and Triangle
        rect.IsOverlapping(triangle);
        triangle.IsOverlapping(rect);

        // This will throw since we have no calculation between Circle and Triangle.
        circle.IsOverlapping(triangle);
    }

    private static bool IsOverlapping(Rectangle rectangle, Triangle triangle)
    {
        // Do specialised geometry
        return true;
    }
}

这应该是解决您问题的简洁快速(无反射)的解决方案。

此解决方案的一个缺点是您必须使用该方法“声明”计算方法AddCalculation

于 2012-11-22T12:48:12.597 回答