2010-01-15 13 views
10

Ich schreibe eine kleine Datenstrukturen Bibliothek in C#, und ich stoße auf ein architektonisches Problem. Ich eine Klasse, die das Besuchermuster implementiert, und es gibt viele mögliche Implementierungen der Besucher im Wesentlichen:Wie simuliere ich anonyme Klassen in C#

public interface ITreeVisitor<T, U> 
{ 
    U Visit(Nil<T> s); 
    U Visit(Node<T> s); 
} 

public abstract class Tree<T> : IEnumerable<T> 
{ 
    public readonly static Tree<T> empty = new Nil<T>(); 
    public abstract U Accept<U>(ITreeVisitor<T, U> visitor); 
} 

public sealed class Nil<T> : Tree<T> 
{ 
    public override U Accept<U>(ITreeVisitor<T, U> visitor) { return visitor.Visit(this); } 
} 

public sealed class Node<T> : Tree<T> 
{ 
    public Tree<T> Left { get; set; } 
    public T Value { get; set; } 
    public Tree<T> Right { get; set; } 

    public override U Accept<U>(ITreeVisitor<T, U> visitor) { return visitor.Visit(this); } 
} 

Immer wenn ich in einem Besucher weitergeben will, muss ich einen Besucher-Klasse erstellen, implementieren die Schnittstelle, und Pass es wie folgt aus:

class InsertVisitor<T> : ITreeVisitor<T, Tree<T>> where T : IComparable<T> 
{ 
    public T v { get; set; }; 

    public Tree<T> Visit(Nil<T> s) 
    { 
     return new Node<T>() { Left = Tree<T>.empty, Value = v, Right = Tree<T>.empty }; 
    } 

    public Tree<T> Visit(Node<T> s) 
    { 
     switch (v.CompareTo(s.Value)) 
     { 
      case -1: return new Node<T>() { Left = Insert(v, s.Left), Value = s.Value, Right = s.Right }; 
      case 1: return new Node<T>() { Left = s.Left, Value = s.Value, Right = Insert(v, s.Right) }; 
      default: return s; 
     } 
    } 
} 

public static Tree<T> Insert<T>(T value, Tree<T> tree) where T : IComparable<T> 
{ 
    return tree.Accept<Tree<T>>(new InsertVisitor<T>() { v = value }); 
} 

ich mag nicht so viel vorformulierten Code zu schreiben, weil es sehr chaotisch wird, wenn Sie eine nicht-triviale Anzahl der Besucher-Implementierungen haben.

ich etwas ähnliches wie anonymous classes Java (Concept-Code) schreiben will:

public static Tree<T> Insert<T>(T v, Tree<T> tree) where T : IComparable<T> 
{ 
    return tree.Accept<Tree<T>>(new InsertVisitor<T>() 
     { 
      public Tree<T> Visit(Nil<T> s) { return new Node<T>() { Left = Tree<T>.empty, Value = v, Right = Tree<T>.empty }; } 
      public Tree<T> Visit(Node<T> s) 
      { 
       switch (v.CompareTo(s.Value)) 
       { 
        case -1: return new Node<T>() { Left = Insert(v, s.Left), Value = s.Value, Right = s.Right }; 
        case 1: return new Node<T>() { Left = s.Left, Value = s.Value, Right = Insert(v, s.Right) }; 
        default: return s; 
       } 
      } 
     }; 
} 

Gibt es eine Möglichkeit anonyme Klassen mit Interface-Implementierungen in C# zu simulieren?

+2

Vielleicht möchten erklären, was eine anonyme Schnittstelle ist. Ich fürchte, ich habe keine Ahnung, was es bedeutet. – Noldorin

+2

@Noldorin: anonym * Schnittstelle * ist nicht die beste Wahl von Wörtern, was ich meine, war anonym * Klasse *. Es gibt eine Funktion in Java, wo Sie Interfaces im laufenden Betrieb implementieren können, ohne eine benannte Klasse zu benötigen - ich möchte etwas Ähnliches in C# machen. – Juliet

+0

Sie sind sicher, dass Sie mit Delegierten nicht auskommen können? –

Antwort

7

Sie können den "Behaviour" -Teil der Klasse von Methoden, die für eine Schnittstelle definiert wurden, zu Delegaten ändern, die zur richtigen Zeit aufgerufen werden, und einen neuen Besucher erstellen, indem Sie neue Delegaten übergeben und damit anonyme Funktionen für die Arbeit von anonyme Klassen.

Sketch-Code (nicht getestet, können Sie gegebenenfalls reinigen):

class CustomVisitor<T> : ITreeVisitor<T, Tree<T>> where T : IComparable<T> 
{ 
    public T v { get; set; }; 
    public Func<Nil<T>, Tree<T>> VisitNil { get; set; } 
    public Func<Node<T>, Tree<T>> VisitNode { get; set; } 

    public Tree<T> Visit(Nil<T> s) { return VisitNil(s); } 
    public Tree<T> Visit(Node<T> s) { return VisitNode(s); } 
} 

public static Tree<T> Insert<T>(T v, Tree<T> tree) where T : IComparable<T> 
{ 
    return tree.Accept<Tree<T>>(new CustomVisitor<T> { 
     VisitNil = s => 
      return new Node<T>() { Left = Tree<T>.empty, Value = v, Right = Tree<T>.empty }; } 
     VisitNode = s => 
     { 
      switch (v.CompareTo(s.Value)) 
      { 
       case -1: return new Node<T>() { Left = Insert(v, s.Left), Value = s.Value, Right = s.Right }; 
       case 1: return new Node<T>() { Left = s.Left, Value = s.Value, Right = Insert(v, s.Right) }; 
       default: return s; 
      } 
     } 
    }); 
} 
+0

+1, + antwort: oh wow, das war einfacher als ich dachte :) Ich war besorgt, dass ich meinen Code mit 'if (tree is Nil) {...} else {...}, nur um die gesamte Baum-Traversal-Logik in der gleichen Methode zu halten. Sehr geschätzt! – Juliet

+1

Dies ist ein gutes Beispiel dafür, wie funktionale Programmierkonzepte objektorientierten Sprachen einen Mehrwert verleihen können. =) –