2015-01-22 16 views
8

Tôi phải thiết kế một giải pháp cho một nhiệm vụ, và tôi muốn sử dụng một cái gì đó về mặt lý thuyết tương tự như ExpressionVisitor của C#.Động lực của việc triển khai C# ExpressionVisitor là gì?

Vì sự tò mò, tôi đã mở các nguồn .NET cho ExpressionVisitor để xem xét. Từ thời điểm đó, tôi đã tự hỏi tại sao nhóm .NET thực hiện khách truy cập như họ đã làm.

Ví dụ MemberInitExpression.Accept trông như thế này:

protected internal override Expression Accept(ExpressionVisitor visitor) { 
    return visitor.VisitMemberInit(this); 
} 

tôi - có lẽ Noob - Câu hỏi là: nó có ý nghĩa gì? Tôi có nghĩa là không nên chính phương pháp chấp nhận chịu trách nhiệm về cách nó thực hiện việc truy cập trong chính nó? Tôi có nghĩa là tôi đã mong đợi một cái gì đó như thế này (loại bỏ khả năng hiển thị internal là Overridable từ bên ngoài):

protected override Expression Accept(ExpressionVisitor visitor) { 
    return this.Update(
      visitor.VisitAndConvert(this.NewExpression, "VisitMemberInit"), 
      visitor.Visit(this.Bindings, VisitMemberBinding) 
      ); 
} 

Tuy nhiên, mã này nằm ở cùng VisitMemberInit phương pháp cơ sở ExpressionVisitor 's, mà được gọi là từ MemberInitExpression.Accept. Vì vậy, có vẻ như không phải bất kỳ lợi ích nào của việc triển khai Accept tại đây.

Tại sao không xử lý cây trong cơ sở ExpressionVisitor và quên tất cả các phương pháp Accept?

Tôi hy vọng bạn hiểu điểm của tôi và hy vọng ai đó có thể làm sáng tỏ một số động lực đằng sau triển khai này. Có lẽ tôi không hiểu mẫu khách truy cập ở tất cả? ...

Trả lời

2

Một khách truy cập có thể ghi đè cách bất kỳ biểu thức nào được truy cập. Nếu đề xuất của bạn được thực hiện ở tất cả các nơi, người truy cập sẽ không bao giờ được gọi. Tất cả logic truy cập sẽ nằm trong phần ghi đè của Accept. Không phải mã BCL không thể ghi đè phương thức này.

Nếu bạn viết visitor.Visit((Expression)this.SomeExpression) (giống như bạn làm trong câu hỏi) thì bạn sẽ thực hiện công văn động trên loại SomeExpression như thế nào? Bây giờ khách truy cập phải thực hiện công văn động. Lưu ý rằng đoạn mã thứ 2 của bạn làm cho giả định đơn giản hóa rằng tất cả các biểu thức con được truy cập có loại đã biết. Hãy thử viết mã cho BinaryExpression để xem ý tôi là gì.

Có lẽ tôi không hiểu điều gì đó nhưng đề xuất này không có ý nghĩa.

Mục đích của phương pháp Accept là tối ưu hóa hiệu suất. Mỗi chấp nhận là một cuộc gọi ảo mà là khá rẻ. Thay thế sẽ là có một chuyển đổi lớn trong khách truy cập trên các loại biểu thức (mà là một enum). Đó có thể là chậm hơn.

+0

Cảm ơn. Tôi hiểu điểm thực hiện. Dù sao thì tôi cũng đề cập đến đề xuất của tôi rằng phương thức Accept sẽ không ở bên trong, chỉ được bảo vệ ảo. Tôi sẽ sửa câu hỏi của tôi, xin lỗi vì đã gây hiểu nhầm. –

+1

Điều đó không hữu ích bởi vì bạn có thể có nhiều khách truy cập làm những việc khác nhau. Bạn không thể mong đợi mã người dùng ghi đè phương thức này. – usr

+0

Đối với chỉnh sửa của bạn, năng động, di chuyển được thực hiện ngay bây giờ trong phương thức ExpressionVisitor.Visit, trong một công tắc lớn tôi nghĩ (dựa trên NodeType), nhưng tôi phải kiểm tra. –

2

Mẫu khách truy cập cho phép thuật toán về cơ bản được tách biệt với cấu trúc mà nó đang hoạt động. Trong trường hợp này cấu trúc nó hoạt động trên là cây biểu thức.

Lưu ý rằng phương thức Accept trong khách truy cập là virtual. Điều này có nghĩa là chúng ta có thể ghi nhớ các cách triển khai khác nhau của ExpressionVisitor làm những việc khác nhau với một cây biểu thức (và, quả thật vậy, có các triển khai khác nhau). Và chúng ta có thể làm điều này mà không thay đổi bất kỳ mã nào trong các lớp biểu thức của cây.

Ví dụ về triển khai khách truy cập khác nhau có thể giống như có một khách truy cập biến cây biểu thức trở lại thành chuỗi đại diện cho mã C# (hoặc có thể mã bằng ngôn ngữ khác).

+0

Vâng tôi hiểu điều đó, và đó là lý do tại sao tôi hỏi về các phương pháp của Accpet trong các lớp Expression. Nhưng @usr đã chỉ ra rằng đó là vì lý do hiệu suất. –

+1

Nó không thực sự cho hiệu suất, mặc dù, đó là về nơi mã sống. Anh ta đúng về công văn động, nhưng đó không phải là lý do bạn không thể kéo thực hiện 'Visit' thẳng vào phương thức' Accept'. Có hai cấp độ của công văn động đang diễn ra ở đây. Một là gửi đi tùy thuộc vào loại nút đang được truy cập, nhưng thứ hai là gửi đến các cài đặt khách truy cập khác nhau (không yêu cầu thay đổi đối với các loại nút). – Kyle

+0

Có ngay bây giờ tôi hiểu rằng triển khai được đề xuất của tôi là thiết kế tồi vì nó mã hóa quá trình truyền tải biểu thức. Câu hỏi khác hoàn toàn là về lý do tại sao có các phương thức Accept thay vì thực hiện các công văn động trong một switch lớn trong ExpressionVisitor cơ bản. Thật không may tôi không thể chấp nhận hai câu trả lời, nhưng cảm ơn bạn anyway cho gợi ý của bạn. –

Các vấn đề liên quan