2012-11-22 33 views
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,但我仍然有問題。基本上我想重載基於獨特的無序參數集(我知道這是不可能的,所以尋找解決方法)。

+1

你見過[這些](http://stackoverflow.com/questions/tagged/c%23+double-dispatch)嗎? – AakashM

+0

出於興趣,您爲什麼在這裏使用'dynamic'而不是僅僅切換類型? (或提供方法覆蓋?) – Rawling

+0

因爲我的類型被引用爲「形狀」。我需要運行時對象類型來分派正確的方法。 – davenewza

回答

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並試圖獲得一個匹配時你在Shape調用IsOverlapping

你使用這樣的:

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方法「聲明」計算方法。