2012-05-25 50 views
6

Cập nhật: Đây không còn là một vấn đề từ C# 6, trong đó đã giới thiệu các nhà điều hành nameof để giải quyết các tình huống như vậy (xem MSDN).biểu thức Lambda cho cấu trúc lại an toàn ArgumentException

Lưu ý: Tham khảo “Getting names of local variables (and parameters) at run-time through lambda expressions” để biết khái quát về câu hỏi này cũng như một số câu trả lời.

Tôi thích ý tưởng của việc sử dụng biểu thức lambda để tạo triển khai cấu trúc lại an toàn của giao diện INotifyPropertyChanged, sử dụng mã tương tự như cung cấp bởi Eric De Carufel.

Tôi đang thử nghiệm việc triển khai một cái gì đó tương tự để cung cấp tên thông số cho một ArgumentException (hoặc các lớp dẫn xuất của nó) theo cách tái cấu trúc an toàn.

tôi đã xác định các phương pháp hữu ích sau đây để thực hiện null kiểm tra:

public static void CheckNotNull<T>(Expression<Func<T>> parameterAccessExpression) 
{ 
    Func<T> parameterAccess = parameterAccessExpression.Compile(); 
    T parameterValue = parameterAccess(); 
    CheckNotNull(parameterValue, parameterAccessExpression); 
} 

public static void CheckNotNull<T>(T parameterValue, 
    Expression<Func<T>> parameterAccessExpression) 
{ 
    if (parameterValue == null) 
    { 
     Expression bodyExpression = parameterAccessExpression.Body; 
     MemberExpression memberExpression = bodyExpression as MemberExpression; 
     string parameterName = memberExpression.Member.Name; 
     throw new ArgumentNullException(parameterName); 
    } 
} 

Đối số xác nhận sau đó có thể được thực hiện một cách cấu trúc lại an toàn bằng cách sử dụng cú pháp sau:

CheckNotNull(() => arg);   // most concise 
CheckNotNull(arg,() => args);  // equivalent, but more efficient 

mối quan tâm của tôi nói dối trong các dòng sau:

Expression bodyExpression = parameterAccessExpression.Body; 
MemberExpression memberExpression = bodyExpression as MemberExpression; 

A MemberExpression thể hiện “truy cập một trường hoặc thuộc tính”. Nó được đảm bảo hoạt động chính xác trong trường hợp INotifyPropertyChanged, vì biểu thức lambda sẽ là quyền truy cập thuộc tính.

Tuy nhiên, trong mã của tôi ở trên, biểu thức lambda là một tham số ngữ nghĩa, không phải truy cập trường hoặc thuộc tính. Lý do duy nhất mã hoạt động là trình biên dịch C# thúc đẩy bất kỳ biến cục bộ nào (và các tham số) được ghi lại trong các hàm ẩn danh cho các biến mẫu trong một lớp do trình biên dịch tạo ra đằng sau hậu trường. Điều này được chứng thực bởi Jon Skeet.

Câu hỏi của tôi là: Hành vi này có phải là một chi tiết triển khai có thể thay đổi trong các triển khai thay thế hoặc các phiên bản tương lai của khung công tác không? Cụ thể, có thể có các môi trường nơi parameterAccessExpression.Body is MemberExpression trả lại false?

+1

Điều gì về 'CheckNotNull (() => ((đối tượng) 5) dưới dạng chuỗi);'? – leppie

+1

Cá nhân tôi quyết định rằng bất cứ điều gì 'Contract.Requires ' nào là đủ tốt cho tôi. Nếu điều kiện tiên quyết thất bại, đó là lỗi. Không cần phải băn khoăn về các chi tiết. – CodesInChaos

+0

@CodeInChaos: Còn thư viện sẽ được cung cấp để tiêu thụ cho bên thứ ba thì sao? Dự kiến ​​rằng các phương thức công khai sẽ thực hiện kiểm tra đối số và ném 'ArgumentException' với tên thông số chính xác. – Douglas

Trả lời

0

Đóng cửa: Như bạn đã nói, để truy cập thông số, trình biên dịch C# (có, cụ thể là trình biên dịch) tạo một lớp đóng chứa các trường mẫu để lưu giá trị của biến tham số đã capture của bạn. Có thể thay đổi này với các phiên bản tương lai của trình biên dịch C# không? Chắc chắn rồi. Có thể trong một phiên bản tương lai của C#, các lớp đóng được tạo ra sẽ có các biến được đặt tên ngẫu nhiên vì tên này không thực sự quan trọng trong thời gian chạy. Hơn nữa, mã bạn có thể không hoạt động với các ngôn ngữ .NET khác. Bạn sẽ nhận thấy rằng VB .NET tạo ra các cây biểu thức và các lớp đóng cửa hơi khác với C# đôi khi ...

Tôi không chắc liệu triển khai hiện tại của bạn có hoạt động cho các cấu trúc hay không (mặc dù tôi có thể nhớ sai ... tình hình tôi đang nghĩ đến việc giao dịch với boxing chỉ có thể áp dụng cho Expression<Func<T, object>> (đọc, hãy tự mình thử)

Dù sao ... tất cả điều này ... sẽ thay đổi trong các phiên bản tương lai của C#?Nếu có, bạn có thể thay đổi triển khai nội bộ của mình để xử lý có thể ...

Về hiệu suất: hãy thực sự cẩn thận tại đây. Bạn đã nói nó sẽ hiệu quả hơn để vượt qua hai đối số, do đó bạn không cần phải biên dịch và đánh giá lambda .... nhưng chỉ để được rõ ràng, bạn đang nói về một 15 đến 30ms nhấn ở đây mỗi khi bạn biên dịch và đánh giá.

+0

Tôi chấp nhận câu trả lời này vì nó làm tăng một số điểm hợp lệ (bao gồm khả năng các biến được thăng chức được đổi tên và không tương thích với các ngôn ngữ .NET khác). Để tham khảo, tôi cũng đã ghi lại nghiên cứu của mình về [thiếu tiêu chuẩn hóa hành vi này] (http://stackoverflow.com/a/11071271/1149773) theo câu hỏi khác của tôi. – Douglas

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