2010-01-15 7 views
10

Tôi đang viết một thư viện cấu trúc dữ liệu nhỏ trong C# và tôi đang gặp sự cố kiến ​​trúc. Về cơ bản tôi có một lớp mà thực hiện mô hình khách truy cập, và có nhiều triển khai có thể truy cập:Làm cách nào để mô phỏng các lớp ẩn danh trong 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); } 
} 

Bất cứ lúc nào tôi muốn vượt qua trong một vị khách, tôi phải tạo ra một lớp khách truy cập, thực hiện giao diện, và vượt qua nó như thế này:

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

Tôi không thích viết nhiều mã bản mẫu, vì nó rất lộn xộn khi bạn có số lượng khách truy cập không nhỏ.

Tôi muốn viết một cái gì đó tương tự như anonymous classes Java (mã khái niệm):

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

Có cách nào để mô phỏng lớp vô danh với việc triển khai giao diện trong C#?

+2

Có thể muốn giải thích những gì một giao diện vô danh là. Tôi sợ tôi không biết nó có ý nghĩa gì. – Noldorin

+2

@Noldorin: giao diện ẩn danh * không phải là lựa chọn tốt nhất của từ, ý tôi là ẩn danh * lớp *. Có một tính năng trong Java, nơi bạn có thể thực hiện các giao diện khi đang chạy mà không cần một lớp có tên - Tôi muốn làm một cái gì đó tương tự trong C#. – Juliet

+0

Bạn chắc chắn rằng bạn không thể làm gì với các đại biểu? –

Trả lời

7

Bạn có thể thay đổi phần "hành vi" của lớp từ các phương thức được xác định đối với giao diện cho đại biểu được gọi vào thời điểm thích hợp và tạo khách truy cập mới bằng cách chuyển giao đại biểu mới - do đó tranh thủ chức năng ẩn danh để thực hiện công việc các lớp ẩn danh.

đang Sketch (không dự thi, bạn có thể dọn dẹp nếu thích hợp):

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, + trả lời: oh wow, đó là đơn giản hơn tôi nghĩ :) Tôi đã lo lắng rằng sẽ cần phải viết lại mã của tôi bằng cách sử dụng 'if (cây là Nil) {...} else {...} 'chỉ để giữ cho tất cả logic cây ngang nhau chứa trong cùng một phương thức. Nhiều đánh giá cao! – Juliet

+1

Đây là một ví dụ tốt về cách các khái niệm lập trình chức năng có thể thêm giá trị cho các ngôn ngữ hướng đối tượng. =) –