2

次のShape階層があります。

public abstract class Shape
{ ... }

public class Rectangle : Shape
{ ... }

public class Circle : Shape
{ ... }

public class Triangle : Shape
{ ... }

2つの形状が交差しているかどうかを判断するために、次の機能を実装しました。次の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;
    }
}

これは単にデリゲートを に格納し、を呼び出したDictionaryときに一致するデリゲートを取得しようとします。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;
    }
}

これは、問題に対するきちんとした高速な (リフレクションなしの) 解決策になるはずです。

このソリューションの欠点の 1 つは、メソッドを使用して計算メソッドを「宣言」する必要があることですAddCalculation

于 2012-11-22T12:48:12.597 に答える