2010-01-15 12 views
10

Piszę małą bibliotekę struktur danych w języku C# i mam problem z architekturą. Zasadniczo mam klasy, która implementuje odwiedzający, i istnieje wiele możliwych implementacji odwiedzających:Jak symulować anonimowe klasy w 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); } 
} 

Zawsze chcę przekazać w odwiedzającego, muszę utworzyć klasę odwiedzających, implementować interfejs i przekazać to w ten sposób:

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 }); 
} 

nie lubię pisać, że dużo kodu szablonowe, bo robi się bardzo brudny, gdy masz numer nietrywialne wdrożeń gości.

chcę napisać coś podobnego do anonymous classes Java (kod koncepcja):

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; 
       } 
      } 
     }; 
} 

Czy istnieje jakiś sposób, aby symulować anonimowych klas z implementacji interfejsu w C#?

+2

chcieć wyjaśnić co anonimowy interfejs. Obawiam się, że nie mam pojęcia, co to znaczy. – Noldorin

+2

@Noldorin: anonimowy * interfejs * nie jest najlepszym wyborem słów, mam na myśli anonimową * klasę *. W Javie jest funkcja, w której można implementować interfejsy w locie bez potrzeby używania nazwanej klasy - chciałbym zrobić coś podobnego w języku C#. – Juliet

+0

Jesteś pewien, że nie możesz zrobić z delegatami? –

Odpowiedz

7

Można zmienić "zachowanie" części klasy z metod zdefiniowanych w interfejsie na delegatów wywoływanych w odpowiednim czasie, i utworzyć nowego gościa, przekazując nowych delegatów - tym samym rejestrując anonimowe funkcje do wykonania pracy anonimowe zajęcia.

kod szkic (nie testowane, można oczyścić za stosowne):

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, + odpowiedź: o wow, to było prostsze niż myślałem :) Martwiłem się, że będę musiał przepisać mój kod za pomocą 'if (tree is Nil) {...} else {...} 'po prostu dla zachowania logiki drzewa zawartej w tej samej metodzie. Bardzo doceniane! – Juliet

+1

Jest to dobry przykład tego, jak funkcjonalne koncepcje programistyczne mogą dodawać wartości językom obiektowym. =) –