トリプルディスパッチ

トリプルディスパッチの例。

// Shape.cs
public abstract partial class Shape
{
  public static void Check(Shape sh1, Shape sh2, Shape sh3)
  {
    sh1.Check(sh2, sh3);
  }
  public abstract void Check(Shape sh1, Shape sh2);
  public void Show(Shape sh1, Shape sh2)
  {
    System.Console.WriteLine("{0} {1} {2}",
      GetType().Name, sh1.GetType().Name, sh2.GetType().Name);
  }
}

// Circle.cs
public abstract partial class Shape
{
  public abstract void Check(Circle sh1, Shape sh2);
  public abstract void Check(Circle sh1, Circle sh2);
}
public partial class Circle : Shape
{
  public override void Check(Shape sh1, Shape sh2) { sh1.Check(this, sh2); }
  public override void Check(Circle sh1, Shape sh2) {sh2.Check(sh1, this);}
  public override void Check(Circle sh1, Circle sh2) { Show(sh1, sh2); }
}

// Rect.cs
public abstract partial class Shape
{
  public abstract void Check(Rect sh1, Shape sh2);
  public abstract void Check(Rect sh1, Circle sh2);
  public abstract void Check(Rect sh1, Rect sh2);
}
public partial class Circle : Shape
{
  public override void Check(Rect sh1, Shape sh2) { sh2.Check(sh1, this); }
  public override void Check(Rect sh1, Circle sh2) { sh1.Check(this, sh2); }
  public override void Check(Rect sh1, Rect sh2) { sh1.Check(sh2, this); }
}
public partial class Rect : Shape
{
  public override void Check(Shape sh1, Shape sh2) { sh1.Check(this, sh2); }
  public override void Check(Circle sh1, Shape sh2) { sh2.Check(this, sh1); }
  public override void Check(Circle sh1, Circle sh2) { Show(sh1, sh2); }
  public override void Check(Rect sh1, Shape sh2) { sh2.Check(sh1, this); }
  public override void Check(Rect sh1, Circle sh2) { Show(sh1, sh2); }
  public override void Check(Rect sh1, Rect sh2) { Show(sh1, sh2); }
}

// Polygon.cs
public abstract partial class Shape
{
  public abstract void Check(Polygon sh1, Shape sh2);
  public abstract void Check(Polygon sh1, Circle sh2);
  public abstract void Check(Polygon sh1, Rect sh2);
  public abstract void Check(Polygon sh1, Polygon sh2);
}
public partial class Circle : Shape
{
  public override void Check(Polygon sh1, Shape sh2) { sh2.Check(sh1, this); }
  public override void Check(Polygon sh1, Circle sh2) { sh1.Check(this, sh2); }
  public override void Check(Polygon sh1, Rect sh2) { sh1.Check(sh2, this); }
  public override void Check(Polygon sh1, Polygon sh2) { sh1.Check(sh2, this); }
}
public partial class Rect : Shape
{
  public override void Check(Polygon sh1, Shape sh2) { sh2.Check(sh1, this); }
  public override void Check(Polygon sh1, Circle sh2) { sh1.Check(this, sh2); }
  public override void Check(Polygon sh1, Rect sh2) { sh1.Check(this, sh2); }
  public override void Check(Polygon sh1, Polygon sh2) { sh1.Check(sh2, this); }
}
public partial class Polygon : Shape
{
  public override void Check(Shape sh1, Shape sh2) { sh1.Check(this, sh2); }
  public override void Check(Circle sh1, Shape sh2) { sh2.Check(this, sh1); }
  public override void Check(Circle sh1, Circle sh2) { Show(sh1, sh2); }
  public override void Check(Rect sh1, Shape sh2) { sh2.Check(this, sh1); }
  public override void Check(Rect sh1, Circle sh2) { Show(sh1, sh2); }
  public override void Check(Rect sh1, Rect sh2) { Show(sh1, sh2); }
  public override void Check(Polygon sh1, Shape sh2) { sh2.Check(sh1, this); }
  public override void Check(Polygon sh1, Circle sh2) { Show(sh1, sh2); }
  public override void Check(Polygon sh1, Rect sh2) { Show(sh1, sh2); }
  public override void Check(Polygon sh1, Polygon sh2) { Show(sh1, sh2); }
}

// Program.cs
public class Test
{
  [STAThread]
  public static void Main(string[] args)
  {
    List<Shape> l = new List<Shape>();
    foreach (Type t in Assembly.GetExecutingAssembly().GetTypes())
      if (t.IsSubclassOf(typeof(Shape))) 
        l.Add((Shape)Activator.CreateInstance(t));
    foreach (Shape s1 in l)
      foreach (Shape s2 in l)
        foreach (Shape s3 in l)
          Shape.Check(s1, s2, s3);
  }
}


2次元オブジェクト(丸、四角、多角形)の衝突判定を考えてみよう。前にC++でダブルディスパッチの例を書いた。 C#でもトリプルディスパッチを上記のようにかける。特徴は、

  • 2次元オブジェクトの種類が増えても、partial classを活用しているため、それまでの具象クラスのファイルは修正不要。
  • 2次元オブジェクトの種類が増えても、実行速度は不変。
  • ダブルでディスパッチでも、それ以上でも同様にかける。
  • Checkを再度呼び出す処理は、自動で生成可能。ただし、VS2005を使えば、書くのも簡単。