2009-10-09 45 views
36

Gần đây tôi đã tìm thấy rằng chúng tôi có thể sử dụng ?? toán tử để kiểm tra null. Vui lòng kiểm tra mẫu mã bên dưới:tại sao chúng ta thích? đến ?? toán tử trong C#?

var res = data ?? new data(); 

Đây chính là tương tự như

var res = (data==null) ? new data() : data ; 

Tôi đã kiểm tra kho lưu trữ nguồn dự án toàn bộ của tôi và một số dự án mã nguồn mở khác. Và nhà điều hành ?? này không bao giờ được sử dụng.

Tôi chỉ tự hỏi có lý do nào đằng sau vấn đề này, chẳng hạn như vấn đề về hiệu suất hay gì đó không?

EDIT:

tôi chỉ cập nhật mẫu mã của tôi dựa trên những ý kiến ​​từ đệ quy & Anton. Đó là một sai lầm trong bất cẩn. :(

+21

Một lý do là ppl có thể không được nhận biết! – vpram86

+1

phải là "var res = (dữ liệu! = Null)? Dữ liệu: dữ liệu mới();" trong mẫu của bạn –

+2

@Rubens, chỉ ngớ ngẩn và không đúng. – kenny

Trả lời

53

Các vô điều hành liên hiệp là rõ ràng hơn nhiều khi kiểm tra null, đó là mục đích chính của nó. Nó cũng có thể bị xiềng xích.

object a = null; 
object b = null; 
object c = new object(); 
object d = a ?? b ?? c; //d == c. 

Trong khi điều hành được giới hạn để null kiểm tra, các nhà điều hành ternary là không. Ví dụ

bool isQuestion = true; 
string question = isQuestion ? "Yes" : "No"; 

tôi nghĩ mọi người chỉ không nhận thức của người điều khiển vô liên hiệp để họ sử dụng các nhà điều hành ternary để thay thế. ternary tồn tại trước C# trong hầu hết các ngôn ngữ phong cách C vì vậy nếu bạn không biết C# bên trong và ra ngoài và/hoặc bạn lập trình bằng ngôn ngữ khác, te rnary là một sự lựa chọn tự nhiên. Nếu bạn đang kiểm tra null mặc dù, sử dụng toán tử kết hợp null, nó được thiết kế cho điều đó, và IL được tối ưu hóa một chút (so sánh với một if nếu khác).

Dưới đây là một ví dụ so sánh việc sử dụng của mỗi

object a = null; 
object b = null; 
object c = null; 

object nullCoalesce = a ?? b ?? c; 

object ternary = a != null ? a : b != null ? b : c; 

object ifThenElse; 

if (a != null) 
    ifThenElse = a; 
else if (b != null) 
    ifThenElse = b; 
else if (c != null) 
    ifThenElse = c; 

Thứ nhất, chỉ cần nhìn vào cú pháp for null liên hiệp, đó là cách rõ ràng hơn. Ternary thực sự khó hiểu. Bây giờ cho phép nhìn vào IL

Null liên hiệp Chỉ

.entrypoint 
.maxstack 2 
.locals init (
    [0] object a, 
    [1] object b, 
    [2] object c, 
    [3] object nullCoalesce) 
L_0000: ldnull 
L_0001: stloc.0 
L_0002: ldnull 
L_0003: stloc.1 
L_0004: newobj instance void [mscorlib]System.Object::.ctor() 
L_0009: stloc.2 
L_000a: ldloc.0 
L_000b: dup 
L_000c: brtrue.s L_0015 
L_000e: pop 
L_000f: ldloc.1 
L_0010: dup 
L_0011: brtrue.s L_0015 
L_0013: pop 
L_0014: ldloc.2 
L_0015: stloc.3 
L_0016: ldloc.3 
L_0017: call void [mscorlib]System.Console::WriteLine(object) 
L_001c: ret 

ternary Chỉ

.entrypoint 
.maxstack 2 
.locals init (
    [0] object a, 
    [1] object b, 
    [2] object c, 
    [3] object ternary) 
L_0000: ldnull 
L_0001: stloc.0 
L_0002: ldnull 
L_0003: stloc.1 
L_0004: newobj instance void [mscorlib]System.Object::.ctor() 
L_0009: stloc.2 
L_000a: ldloc.0 
L_000b: brtrue.s L_0016 
L_000d: ldloc.1 
L_000e: brtrue.s L_0013 
L_0010: ldloc.2 
L_0011: br.s L_0017 
L_0013: ldloc.1 
L_0014: br.s L_0017 
L_0016: ldloc.0 
L_0017: stloc.3 
L_0018: ldloc.3 
L_0019: call void [mscorlib]System.Console::WriteLine(object) 
L_001e: ret 

Nếu Rồi Else Chỉ

.entrypoint 
.maxstack 1 
.locals init (
    [0] object a, 
    [1] object b, 
    [2] object c, 
    [3] object ifThenElse) 
L_0000: ldnull 
L_0001: stloc.0 
L_0002: ldnull 
L_0003: stloc.1 
L_0004: newobj instance void [mscorlib]System.Object::.ctor() 
L_0009: stloc.2 
L_000a: ldloc.0 
L_000b: brfalse.s L_0011 
L_000d: ldloc.0 
L_000e: stloc.3 
L_000f: br.s L_001a 
L_0011: ldloc.1 
L_0012: brfalse.s L_0018 
L_0014: ldloc.1 
L_0015: stloc.3 
L_0016: br.s L_001a 
L_0018: ldloc.2 
L_0019: stloc.3 
L_001a: ldloc.3 
L_001b: call void [mscorlib]System.Console::WriteLine(object) 
L_0020: ret 

IL không phải là một trong những điểm mạnh của tôi, vì vậy có thể ai đó có thể chỉnh sửa câu trả lời của tôi và mở rộng câu trả lời trên đó. Tôi sẽ giải thích lý thuyết của tôi, nhưng tôi không muốn nhầm lẫn bản thân mình và những người khác. Số LOC tương tự cho cả ba, nhưng không phải tất cả các toán tử IL đều có cùng thời gian thực thi.

+7

Nó không phải * toán tử * ternary, nó chỉ là * một * toán tử ternary. Những gì bạn gọi là được gọi là ** toán tử điều kiện **. –

+1

@ Bob, thats một so sánh tốt đẹp ... điều hành hợp nhất là tốt hơn so với tất cả khi chúng ta đối phó với nulls ... – RameshVel

+3

@ divo: cũng là nhà điều hành duy nhất trong C#: P – Lucas

6

Một lý do tôi có thể nghĩ là toán tử này đã được giới thiệu trong .NET 2.0 nên mã cho .NET 1.1 không thể có.

Tôi đồng ý với bạn, chúng tôi nên sử dụng điều này thường xuyên hơn.

ref link

12

??nhà điều hành (còn được gọi là null-coalescing operator) ít được biết đến hơn so với toán tử bậc ba, vì nó xuất hiện lần đầu với .NET 2.0 và Nullable Types. Lý do không sử dụng nó có thể bao gồm không bắt đầu nhận thức được rằng nó tồn tại hoặc quen thuộc hơn với toán tử bậc ba.

Điều đó nói rằng, việc kiểm tra null không phải là điều duy nhất mà toán tử ternary là tốt, vì vậy nó không phải là một sự thay thế cho nó, giống như một sự thay thế tốt hơn cho một nhu cầu rất cụ thể. :)

1

Tôi nghĩ đó chỉ là thói quen từ các ngôn ngữ khác. AFAIK, ?? toán tử không được sử dụng trong bất kỳ ngôn ngữ nào khác.

+2

Tôi biết điều này là siêu cũ, nhưng tôi muốn bình luận và nói rằng nó cũng được sử dụng trong Perl như của v5.10, chỉ với '//' chứ không phải là '??'. Ví dụ (được lấy từ [wikipedia] (http://en.wikipedia.org/wiki/Null_coalescing_operator#Perl)): '$ might_null_value // $ value_if_null' –

0

Tôi đã có thể nghĩ tương đương với

var res = data ?? data.toString(); 

sẽ

var res = (data!=null) ? data : data.toString(); 
2

Một lý do (như những người khác đã chạm) có khả năng là thiếu nhận thức. Nó cũng có thể là (như trong trường hợp của riêng tôi), một mong muốn giữ cho số lượng các phương pháp tiếp cận để làm những việc tương tự trong một cơ sở mã xuống càng nhiều càng tốt. Vì vậy, tôi có xu hướng sử dụng toán tử bậc ba cho tất cả các tình huống nếu-a-điều kiện-được-đáp-làm-điều-khác-làm-được-làm-điều đó.

Ví dụ, tôi thấy hai câu sau đây khá tương tự trên một cấp độ khái niệm:

return a == null ? string.Empty : a;  
return a > 0 ? a : 0; 
4

Dựa trên câu trả lời Bob's

public object nullCoalesce(object a, object b, object c) 
{ 
    return a ?? b ?? c; 
} 
public object ternary(object a, object b, object c) 
{ 
    return a != null ? a : b != null ? b : c; 
} 
public object ifThenElse(object a, object b, object c) 
{ 
    if (a != null) 
     return a; 
    else if (b != null) 
     return b; 
    else 
     return c; 
} 

... đây là IL từ phát hành xây dựng .. .

.method public hidebysig instance object nullCoalesce(
    object a, 
    object b, 
    object c) cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.1 
    L_0001: dup 
    L_0002: brtrue.s L_000b 
    L_0004: pop 
    L_0005: ldarg.2 
    L_0006: dup 
    L_0007: brtrue.s L_000b 
    L_0009: pop 
    L_000a: ldarg.3 
    L_000b: ret 
} 

.method public hidebysig instance object ternary(
    object a, 
    object b, 
    object c) cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.1 
    L_0001: brtrue.s L_000a 
    L_0003: ldarg.2 
    L_0004: brtrue.s L_0008 
    L_0006: ldarg.3 
    L_0007: ret 
    L_0008: ldarg.2 
    L_0009: ret 
    L_000a: ldarg.1 
    L_000b: ret 
} 

.method public hidebysig instance object ifThenElse(
    object a, 
    object b, 
    object c) cil managed 
{ 
    .maxstack 8 
    L_0000: ldarg.1 
    L_0001: brfalse.s L_0005 
    L_0003: ldarg.1 
    L_0004: ret 
    L_0005: ldarg.2 
    L_0006: brfalse.s L_000a 
    L_0008: ldarg.2 
    L_0009: ret 
    L_000a: ldarg.3 
    L_000b: ret 
} 
Các vấn đề liên quan