2012-06-29 23 views
58

câu hỏi cơ bản ở đây - Tôi có nhiều dòng mã giống như thế:Cú pháp tốt nhất cho a = (x == null)? null: x.func()

var a = (long_expression == null) ? null : long_expression.Method(); 

dòng tương tự lặp lại rất nhiều trong chức năng này. long_expression là khác nhau mỗi lần. Tôi đang cố gắng tìm một cách để tránh lặp lại long_expression, nhưng giữ nhỏ gọn này. Một cái gì đó giống như đối diện của operator ??. Đối với thời điểm hiện tại tôi đang xem xét chỉ nhượng bộ và đặt nó trên nhiều dòng như:

var temp = long_expression; 
var a = (temp == null) ? null : temp.Method(); 

Nhưng tôi đã tò mò nếu có một số cú pháp thông minh Tôi không biết về điều đó sẽ làm cho này ngắn gọn hơn.

+3

'var a = temp? null: temp.Method(); 'Tôi nghĩ rằng bạn có nghĩa là để có' var a = temp == null? null: temp.method(); 'Chi tiết nhỏ mặc dù – Sephallia

+0

Tôi nghĩ rằng phương pháp hiện tại của bạn là con đường để đi. –

+3

"Cú pháp tốt nhất" theo ràng buộc "ngôn ngữ == C#" hoặc cho các nội dung như vậy nói chung? Rõ ràng một cái gì đó như thế này có thể được viết khá tốt hơn trong một ngôn ngữ chức năng, sử dụng (applicative) functors/monads và như vậy. – leftaroundabout

Trả lời

77

Vâng, bạn thể sử dụng một phương pháp khuyến nông như thế này:

public static TResult NullOr<TSource, TResult>(this TSource source, 
    Func<TSource, TResult> func) where TSource : class where TResult : class 
{ 
    return source == null ? null : func(source); 
} 

Sau đó:

var a = some_long_expression.NullOr(x => x.Method()); 

Hoặc (tùy thuộc vào phiên bản của C#)

var a = some_long_expression.NullOr(Foo.Method); 

nơi Foo là loại some_long_expression.

Tôi không nghĩ rằng tôi sẽ thực hiện việc này. Tôi chỉ sử dụng phiên bản hai dòng. Nó đơn giản và ít thông minh hơn - và trong khi "thông minh" là niềm vui cho Stack Overflow, nó thường không phải là một ý tưởng tốt cho mã thực.

+1

Hãy đánh tôi! Tôi chuẩn bị đăng một cái gì đó như thế này. –

+0

Rực rỡ. Tôi thích nó! –

+11

+1 cho "Tôi chỉ sử dụng phiên bản hai dòng." – Coeffect

0

Bạn có thể đặt long_expression == null vào một hàm có tên viết tắt và chỉ cần gọi hàm đó mỗi lần.

+1

có nghĩa là, nếu đây là cùng một 'long_expression' ở khắp mọi nơi – poncha

+0

Đó là sự hiểu biết của tôi về câu hỏi. đây không phải là trường hợp à? – Daniel

+3

Điều đó sẽ quá dễ dàng: D Đã cập nhật câu hỏi; nó khác nhau ở khắp mọi nơi. – tenfour

1

Bạn có thể viết một phương pháp mở rộng cho bất cứ điều gì long_expression loại đánh giá để:

public static object DoMethod(this MyType pLongExpression) 
{ 
    return pLongExpression == null ? null : pLongExpression.Method(); 
} 

Đây sẽ là callable trên bất kỳ MyType tham khảo, thậm chí nếu tham khảo đó là null.

+1

Tôi đã luôn không thích các phương thức mở rộng chạy trên các tham chiếu null. Các phương thức gọi trên null nên IMO luôn ném một ngoại lệ tham chiếu Null.Tôi biết các phương thức mở rộng thực tế là các phương thức trợ giúp tĩnh, nhưng điều đó không phải lúc nào cũng rõ ràng đối với người đọc mã. –

+0

Từ OP - biểu thức dài khác nhau mỗi lần (cũng có thể có nghĩa là kiểu trả về). – nawfal

49

Tôi đã tìm thấy this answer sâu sắc.

Bằng cách thực hiện phép gán này, bạn đang truyền bá giá trị rỗng sâu hơn vào hệ thống. Bạn sẽ phải viết lại tình trạng xử lý null của bạn (và một lần nữa và một lần nữa) để xử lý các tuyên truyền này.

Thay vì cho phép thực hiện để tiếp tục với null, thay thế các null với một đại diện tốt hơn (trích dẫn từ liên kết)

  1. Nếu null thể hiện một bộ sưu tập trống, sử dụng một bộ sưu tập trống.
  2. Nếu null đại diện cho một trường hợp ngoại lệ, hãy ném Ngoại lệ.
  3. Nếu giá trị rỗng đại diện cho giá trị vô tình chưa được khởi tạo, hãy khởi chạy nó một cách rõ ràng.
  4. Nếu null đại diện cho một giá trị hợp pháp, hãy kiểm tra nó - hoặc thậm chí tốt hơn sử dụng một NullObject thực hiện một giá trị rỗng.

Cụ thể, thay thế tham chiếu bộ sưu tập rỗng bằng bộ sưu tập trống đã tiết kiệm cho tôi nhiều thử nghiệm null.

+0

Tôi rất đồng ý; nhưng thật không may, trong nhiều tình huống bạn thấy mình làm việc với một API hoặc mã kế thừa sử dụng rất nhiều. –

5
var x = ""; 

/* try null instead */ 
string long_expression = "foo"; 

var a = ((x = long_expression) == null) ? null : x.ToUpper(); 

/* writes "FOO" (or nothing) followed by newline */ 
Console.WriteLine(a); 

Các loại giá trị khởi tạo của x phải phù hợp với loại hình long_expression (ở đây: string). Làm việc với trình biên dịch C# Mono C#, phiên bản 2.10.8.1 (từ gói Debian mono-gmcs = 2.10.8.1-4).

+0

Cái gì? Nó là hợp lệ C#. – CMircea

+0

@MirceaChirea OK, sau đó. Liệu nó * làm việc * trong C#? Tôi không thể kiểm tra ở đây. – PointedEars

+0

có. var chỉ yêu cầu trình biên dịch suy ra loại từ biểu thức; đặc biệt hữu ích với các loại dài như IEnumerable . – CMircea

5

Tôi nghĩ ngôn ngữ C# thực sự có thể sử dụng toán tử mới cho loại logic này - nó khá phổ biến và một toán tử sẽ đơn giản hóa vô số dòng mã.

Chúng tôi cần điều gì đó dọc theo dòng của "??" hoặc "nếu null sau đó" nhà điều hành, nhưng hoạt động như đặc biệt '.' dot toán tử với một điều kiện "nếu không null". Đối với "??", chữ cái đầu tiên '?' giống như phần 'if' của "?:" nếu-thì, và phần thứ hai '?' đại diện cho 'null' như cho các kiểu nullable. Tôi nghĩ chúng ta cần một ".?" toán tử hoạt động giống như '.', ngoại trừ nó chỉ đơn giản là đánh giá null nếu biểu thức bên trái là null thay vì ném một ngoại lệ. Tôi đoán nó sẽ là một "dot không null" nhà điều hành (OK, vì vậy có lẽ ".!?" Sẽ hợp lý hơn, nhưng chúng ta hãy không đi đến đó).

Vì vậy, ví dụ oh-so-chung của bạn có thể bị mất tên biểu tượng dư thừa như thế này:

var a = long_expression.?Method(); 

Có một tấn C# mã ra khỏi đó với kiểm tra rỗng lồng nhau rằng sẽ có lợi rất nhiều. Chỉ cần nghĩ như thế nào đẹp nó sẽ nếu điều này:

if (localObject != null) 
{ 
    if (localObject.ChildObject != null) 
    { 
     if (localObject.ChildObject.ChildObject != null) 
      localObject.ChildObject.ChildObject.DoSomething(); 
    } 
} 

có thể trở thành chỉ:

localObject.?ChildObject.?ChildObject.?DoSomething(); 

Ngoài ra còn có một tấn mã nơi kiểm tra không cho rằng nên có mặt ở đó đang thiếu, dẫn đến lỗi runtime thường xuyên. Vì vậy, thực sự sử dụng '.?' Mới nhà điều hành theo mặc định sẽ loại bỏ các vấn đề như vậy ... Có lẽ không may là chúng tôi không thể đảo ngược hành vi của '.' và '.?' vào thời điểm này, vì vậy chỉ có '.?' mới ném một ngoại lệ nếu bên trái là null - nó sẽ là sạch hơn và hợp lý hơn để đi, nhưng phá vỡ những thay đổi là rất xấu. Mặc dù, người ta có thể lập luận rằng sự thay đổi đặc biệt này có thể sẽ khắc phục được rất nhiều vấn đề ẩn, và khó có thể phá vỡ bất cứ điều gì (chỉ có mã dự kiến ​​một ngoại lệ không được phép bỏ). Ồ, vâng ... người ta luôn có thể mơ ước ...

Nhược điểm duy nhất để kiểm tra giá trị rỗng thực sự là hiệu suất nhỏ, điều chắc chắn nhất là tại sao '.' không kiểm tra null. Nhưng, tôi thực sự nghĩ rằng khả năng chỉ sử dụng '.?' khi bạn quan tâm đến hiệu suất (và biết phía bên trái sẽ không bao giờ là null) sẽ là cách tốt hơn để đi.

Trên một lưu ý tương tự, việc kiểm tra 'bỏ qua' và bỏ qua một bộ sưu tập rỗng cũng sẽ đẹp hơn rất nhiều. Không cần phải bọc hầu hết các câu lệnh 'foreach' với "if (collection! = Null)", hay tệ hơn, phát triển thói quen chung luôn sử dụng các bộ sưu tập rỗng thay vì null ... điều này tốt trong một số trường hợp, nhưng thậm chí tệ hơn vấn đề hiệu suất hơn kiểm tra null khi điều này được thực hiện trong cây đối tượng phức tạp, nơi phần lớn các bộ sưu tập trống (mà tôi đã nhìn thấy rất nhiều). Tôi nghĩ rằng ý định tốt của việc không có 'foreach' kiểm tra cho null để cung cấp hiệu suất tốt hơn đã phản tác dụng trong phần lớn các trường hợp.

Anders, không bao giờ là quá muộn để thực hiện các thay đổi đó và thêm trình chuyển đổi trình biên dịch để bật chúng!

+1

được gọi là hoa tiêu không an toàn. Xem tại đây để được trả lời bởi một thành viên của nhóm ngôn ngữ C#: http://stackoverflow.com/a/3818256/45583 – Fowl

+1

Đối diện của ?? là rõ ràng! ?? đó là những gì các nhà điều hành nên được. –

+0

@DavidB - chúng tôi không nói về điều ngược lại của '??', nhưng phiên bản an toàn không có giá trị '.', Là một toán tử hoàn toàn khác. Tài liệu tham khảo của tôi là '??' chỉ là lý do tại sao '.?' có thể có ý nghĩa, nhưng những người khác dường như đã đi đến cùng một kết luận. –

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