Lợi ích của việc sử dụng toán tử điều kiện?: (Bậc ba)


101

Lợi ích và nhược điểm của toán tử ?: trái ngược với câu lệnh if-else tiêu chuẩn. Những điều rõ ràng là:

Điều hành có điều kiện

  • Ngắn gọn và súc tích hơn khi xử lý các phép so sánh và gán giá trị trực tiếp
  • Có vẻ không linh hoạt như cấu trúc if / else

Nếu / Khác chuẩn

  • Có thể được áp dụng cho nhiều tình huống hơn (chẳng hạn như lệnh gọi hàm)
  • Thường dài không cần thiết

Khả năng đọc có vẻ khác nhau đối với mỗi loại tùy thuộc vào tuyên bố. Trong một thời gian ngắn sau lần đầu tiên tiếp xúc với toán tử?:, Tôi đã mất một khoảng thời gian để hiểu chính xác cách hoạt động của nó. Bạn có khuyên bạn nên sử dụng nó bất cứ khi nào có thể, hoặc gắn bó với nếu / khác cho rằng tôi làm việc với nhiều người không phải là lập trình viên?


8
Bạn đã nắm được ý chính của nó.
Byron Whitlock

1
@Nicholas Knight: Tôi đoán OP có nghĩa là bạn không thể làm, ví dụ: SomeCheck() ? DoFirstThing() : DoSecondThing();- bạn phải sử dụng biểu thức để trả về một giá trị.
Dan Tao

6
Sử dụng nó ở nơi rõ ràng , gắn bó với if / else nếu nó không. Sự rõ ràng của mã nên được bạn cân nhắc chính.
hollsk

8
Bạn đã thấy '??' chưa? Nghiêm túc, nếu bạn nghĩ ternaries là mát mẻ ...
Lào

3
+1 vì không gọi nó đơn giản là "toán tử bậc ba" như nhiều người vẫn làm. Mặc dù nó là toán tử bậc ba duy nhất (trái ngược với toán tử một bậc và nhị phân) trong C #, đó không phải là tên của nó.
John M Gant,

Câu trả lời:


122

Về cơ bản, tôi khuyên bạn chỉ nên sử dụng nó khi câu lệnh kết quả cực kỳ ngắn và thể hiện sự ngắn gọn tăng đáng kể so với if / else tương đương mà không làm mất khả năng đọc.

Ví dụ tốt:

int result = Check() ? 1 : 0;

Ví dụ xấu:

int result = FirstCheck() ? 1 : SecondCheck() ? 1 : ThirdCheck() ? 1 : 0;

5
Cuộc gọi tốt, nhưng đối với hồ sơ, đó là "sự ngắn gọn".
mqp

6
@mquander, bạn chắc chắn về điều đó chứ? merriam-webster.com/dictionary/concise
Byron Whitlock

39
Tôi luôn bắt đầu với một cái đơn giản và làm cho nó phức tạp hơn theo thời gian cho đến khi nó hoàn toàn không thể đọc được.
Jouke van der Maas

9
Khả năng đọc trong ví dụ thứ hai có thể dễ dàng được điều chỉnh với định dạng tốt hơn. Tuy nhiên, như OP đang khuyến nghị, nó phụ thuộc vào tính dễ đọc và tính ngắn gọn so với độ dài.
Nathan Ernst

4
Không phải là một phần câu hỏi của OP, nhưng điều quan trọng cần lưu ý là bạn không returnthể tham gia vào kết quả của phép toán bậc ba. Ví dụ: check() ? return 1 : return 0;will not work, but return check() ? 1 : 0;will. Luôn luôn vui khi tìm thấy những điều kỳ quặc nhỏ này trong lập trình.
CSS

50

Điều này được đề cập khá nhiều bởi các câu trả lời khác, nhưng "đó là một biểu thức" không thực sự giải thích tại sao điều đó lại hữu ích ...

Trong các ngôn ngữ như C ++ và C #, bạn có thể xác định các trường chỉ đọc cục bộ (trong thân phương thức) bằng cách sử dụng chúng. Điều này không thể xảy ra với câu lệnh if / then thông thường vì giá trị của trường chỉ đọc phải được gán trong câu lệnh đơn đó:

readonly int speed = (shiftKeyDown) ? 10 : 1;

không giống như:

readonly int speed;  
if (shifKeyDown)  
    speed = 10;    // error - can't assign to a readonly
else  
    speed = 1;     // error  

Theo cách tương tự, bạn có thể nhúng một biểu thức bậc ba vào mã khác. Ngoài việc làm cho mã nguồn nhỏ gọn hơn (và trong một số trường hợp, kết quả là dễ đọc hơn), nó cũng có thể làm cho mã máy được tạo nhỏ gọn và hiệu quả hơn:

MoveCar((shiftKeyDown) ? 10 : 1);

... có thể tạo ra ít mã hơn so với việc phải gọi cùng một phương thức hai lần:

if (shiftKeyDown)
    MoveCar(10);
else
    MoveCar(1);

Tất nhiên, nó cũng là một biểu mẫu ngắn gọn và tiện lợi hơn (ít nhập hơn, ít lặp lại hơn và có thể giảm nguy cơ lỗi nếu bạn phải sao chép các đoạn mã trong if / else). Trong các trường hợp "kiểu chung" rõ ràng như thế này:

object thing = (reference == null) ? null : reference.Thing;

... nó chỉ đơn giản là đọc / phân tích cú pháp / hiểu (khi bạn đã quen với nó) nhanh hơn so với if / else dài dòng tương đương, vì vậy nó có thể giúp bạn 'mò' mã nhanh hơn.

Tất nhiên, chỉ vì nó hữu ích không có nghĩa là nó là thứ tốt nhất để sử dụng trong mọi trường hợp. Tôi khuyên chỉ nên sử dụng nó cho các đoạn mã ngắn mà ý nghĩa rõ ràng (hoặc rõ ràng hơn) bằng cách sử dụng ?:- nếu bạn sử dụng nó trong mã phức tạp hơn hoặc lồng các toán tử bậc ba vào nhau, nó có thể khiến mã khó đọc một cách khủng khiếp .


@JaminGrey "điều đó không có nghĩa là, khi hằng số được tạo, nó được đặt thành 10 hoặc 1" Bạn có nghĩa là không có nghĩa là vậy? Bình luận không đúng có thể gây nhầm lẫn hơn để C ++ mới lập trình viên hơn so với vấn đề bạn đang cố gắng làm sáng tỏ;)
Clonkex

5
Đối với những độc giả tương lai bắt gặp điều này, bởi " const int speed = (shiftKeyDown)? 10: 1; ", điều đó có nghĩa là khi hằng số được tạo lần đầu tiên , hằng số được đặt thành 10 hoặc 1. Không nghĩa là mỗi lần hằng được truy cập nó sẽ kiểm tra. (Chỉ trong trường hợp một lập trình viên C ++ mới hơn bị nhầm lẫn)
Jamin Grey

2
... hay nói một cách khác, a constlà hằng số, tức là nó không thể thay đổi sau khi câu lệnh mà nó được khai báo đã được thực thi.
Jason Williams

1
@JaminGrey. Có nên không readonly? Tôi luôn nghĩ constcó nghĩa là " được giải quyết tại thời điểm biên dịch và được xếp trong hàng bất cứ nơi nào được sử dụng ".
Nolonar

1
@ColinWiseman, đó là một ví dụ để minh họa cách ?: có thể được sử dụng. Tôi đặc biệt nói rằng chỉ vì bạn có thể làm được, không có nghĩa là nó nhất thiết phải là điều "tốt nhất" để làm trong bất kỳ trường hợp cụ thể nào. Để giải quyết vấn đề đó, người đọc phải sử dụng bộ não của họ mỗi khi họ bắt gặp một trường hợp mà nó có thể hữu ích cho họ.
Jason Williams

14

Tôi thường chọn một toán tử bậc ba khi tôi có nhiều mã trùng lặp.

if (a > 0)
    answer = compute(a, b, c, d, e);
else
    answer = compute(-a, b, c, d, e);

Với toán tử bậc ba, điều này có thể được thực hiện với những điều sau đây.

answer = compute(a > 0 ? a : -a, b, c, d, e); 

12
Cá nhân tôi sẽ làm aVal = a > 0 ? a : -a; answer = compute(aVal,b,c,d,e);Đặc biệt nếu b, c, decần điều trị quá.
corsiKa 22/07/10

10
Tại sao lại sử dụng điều kiện trong ví dụ này? Chỉ cần lấy Abs (a) và gọi compute () một lần.
Ash,

2
Vâng, tôi đã không tạo ra ví dụ tốt nhất. :)
Ryan Bright

Đối với một người mới, điều đó trông không tương đương. Nó có cần phải là answer = compute (a> 0? A, b, c, d, e: -a, b, c, d, e); ?
pbreitenbach

@pbreitenbach: không - đó là vấn đề ưu tiên - đối số đầu tiên compute(...)a > 0 ? a : -1, tất cả được đánh giá riêng biệt với các đối số được phân tách bằng dấu phẩy khác. Dù sao, thật không may, C ++ thiếu ký hiệu mà câu hỏi của bạn đặt ra để xử lý "bộ giá trị" được phân tách bằng dấu phẩy, vì vậy thậm chí a > 0 ? (a, b, c, d, e) : (-a, b, c, d, e)là bất hợp pháp và không có gì tương tự hoạt động mà không có thay đổi đối với computechính nó.
Tony Delroy

12

Tôi thấy nó đặc biệt hữu ích khi thực hiện phát triển web nếu tôi muốn đặt một biến thành một giá trị được gửi trong yêu cầu nếu nó được xác định hoặc thành một giá trị mặc định nào đó nếu không.


3
1 giá trị mặc định trong dev web là một ví dụ tuyệt vời một nơi tốt để sử dụng các nhà điều hành ternary
Byron Whitlock

11

Một cách sử dụng thực sự thú vị là:

x = foo ? 1 :
    bar ? 2 :
    baz ? 3 :
          4;

10
Hãy cẩn thận với điều này trong PHP, toán tử bậc ba liên kết sai cách trong PHP. Về cơ bản, nếu foosai, thì toàn bộ nội dung sẽ được đánh giá là 4 mà không cần thực hiện các bài kiểm tra khác.
Tom Busby

4
@TomBusby - Chà. Một lý do khác để ghét PHP, nếu bạn là người đã ghét PHP.
Todd Lehman

6

Toán tử điều kiện rất tốt cho các điều kiện ngắn, như sau:

varA = boolB ? valC : valD;

Tôi thỉnh thoảng sử dụng nó vì mất ít thời gian hơn để viết một cái gì đó theo cách đó ... thật không may, việc phân nhánh này đôi khi có thể bị nhà phát triển khác duyệt qua mã của bạn bỏ qua. Thêm vào đó, mã thường không ngắn như vậy, vì vậy tôi thường giúp dễ đọc bằng cách đặt? và: trên các dòng riêng biệt, như thế này:

doSomeStuffToSomething(shouldSomethingBeDone()
    ? getTheThingThatNeedsStuffDone()
    : getTheOtherThingThatNeedsStuffDone());

Tuy nhiên, lợi thế lớn khi sử dụng các khối if / else (và lý do tại sao tôi thích chúng hơn) là việc truy cập sau này dễ dàng hơn và thêm một số logic bổ sung vào nhánh,

if (shouldSomethingBeDone()) {
    doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
    doSomeAdditionalStuff();
} else {
doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}

hoặc thêm một điều kiện khác:

if (shouldSomethingBeDone()) {
    doSomeStuffToSomething(getTheThingThatNeedsStuffDone());
    doSomeAdditionalStuff();
} else if (shouldThisOtherThingBeDone()){
    doSomeStuffToSomething(getTheOtherThingThatNeedsStuffDone());
}

Vì vậy, cuối cùng, đó là sự thuận tiện cho bạn bây giờ (ngắn hơn để sử dụng:?) So với sự thuận tiện cho bạn (và những người khác) sau này. Đó là một lời kêu gọi phán xét ... nhưng giống như tất cả các vấn đề về định dạng mã khác, quy tắc thực sự duy nhất là phải nhất quán và tỏ ra nhã nhặn đối với những người phải duy trì (hoặc xếp loại!) Mã của bạn.

(tất cả mã được biên dịch bằng mắt)


5

Một điều cần nhận ra khi sử dụng toán tử bậc ba rằng nó là một biểu thức không phải là một câu lệnh.

Trong các ngôn ngữ chức năng như lược đồ, sự khác biệt không tồn tại:

(nếu (> ab) ab)

Có điều kiện ?: Toán tử "Có vẻ không linh hoạt bằng cấu trúc if / else"

Trong các ngôn ngữ chức năng, nó là như vậy.

Khi lập trình bằng ngôn ngữ mệnh lệnh, tôi áp dụng toán tử bậc ba trong các tình huống mà tôi thường sử dụng các biểu thức (phép gán, câu lệnh điều kiện, v.v.).


5

Mặc dù các câu trả lời trên là hợp lệ và tôi đồng ý với việc khả năng đọc là quan trọng, nhưng có 2 điểm nữa cần xem xét:

  1. Trong C # 6, bạn có thể có các phương thức thân biểu thức.

Điều này làm cho nó trở nên đặc biệt ngắn gọn khi sử dụng dấu ba:

string GetDrink(DayOfWeek day) 
   => day == DayOfWeek.Friday
      ? "Beer" : "Tea";
  1. Hành vi khác khi nói đến chuyển đổi kiểu ngầm định.

Nếu bạn có các loại T1T2cả hai loại đó đều có thể được chuyển đổi hoàn toàn thành T, thì loại bên dưới không hoạt động:

T GetT() => true ? new T1() : new T2();

(bởi vì trình biên dịch cố gắng xác định kiểu của biểu thức bậc ba và không có chuyển đổi giữa T1T2.)

Mặt khác, if/elsephiên bản dưới đây hoạt động:

T GetT()
{
   if (true) return new T1();
   return new T2();
}

bởi vì T1được chuyển đổi thành Tvà vì vậyT2


5

Đôi khi, nó có thể làm cho việc gán giá trị bool thoạt nhìn dễ đọc hơn:

// With
button.IsEnabled = someControl.HasError ? false : true;

// Without
button.IsEnabled = !someControl.HasError;

4

Nếu tôi đang đặt một giá trị và tôi biết nó sẽ luôn là một dòng mã để làm như vậy, tôi thường sử dụng toán tử bậc ba (có điều kiện). Nếu có khả năng mã và logic của tôi sẽ thay đổi trong tương lai, tôi sử dụng if / else vì nó rõ ràng hơn đối với các lập trình viên khác.

Quan tâm hơn nữa đối với bạn có thể là ?? nhà điều hành .


4

Ưu điểm của toán tử điều kiện là nó là một toán tử. Nói cách khác, nó trả về một giá trị. Vì iflà một câu lệnh, nó không thể trả về một giá trị.


4

Tôi khuyên bạn nên hạn chế việc sử dụng toán tử bậc ba (? :) cho phép gán một dòng đơn giản if / else logic. Một cái gì đó giống như mô hình này:

if(<boolCondition>) {
    <variable> = <value>;
}
else {
    <variable> = <anotherValue>;
}

Có thể dễ dàng chuyển đổi thành:

<variable> = <boolCondition> ? <value> : <anotherValue>;

Tôi sẽ tránh sử dụng toán tử bậc ba trong các tình huống yêu cầu if / else if / else, if / else lồng nhau hoặc if / else logic nhánh dẫn đến đánh giá nhiều dòng. Việc áp dụng toán tử bậc ba trong những trường hợp này có thể dẫn đến mã không thể đọc được, khó hiểu và không thể quản lý được. Hi vọng điêu nay co ich.


2

Có một số lợi ích hiệu suất của việc sử dụng? toán tử trong ví dụ. MS Visual C ++, nhưng đây thực sự là một thứ cụ thể cho trình biên dịch. Trình biên dịch thực sự có thể tối ưu hóa nhánh có điều kiện trong một số trường hợp.


2

Kịch bản mà tôi thấy mình nhất khi sử dụng nó là cho các giá trị mặc định và đặc biệt là trả về

return someIndex < maxIndex ? someIndex : maxIndex;

Đó thực sự là những nơi duy nhất tôi thấy nó đẹp, nhưng đối với họ thì tôi thấy vậy.

Mặc dù nếu bạn đang tìm kiếm boolean, điều này đôi khi có thể giống như một việc thích hợp để làm:

bool hey = whatever < whatever_else ? true : false;

Bởi vì nó rất dễ đọc và dễ hiểu, nhưng ý tưởng đó nên luôn được đưa ra để rõ ràng hơn:

bool hey = (whatever < whatever_else);

2

Nếu bạn cần nhiều nhánh với cùng một điều kiện, hãy sử dụng if:

if (A == 6)
  f(1, 2, 3);
else
  f(4, 5, 6);

Nếu bạn cần nhiều nhánh với các điều kiện khác nhau, thì nếu số lượng câu lệnh sẽ trở thành quả cầu tuyết, bạn sẽ muốn sử dụng hàm số ba:

f( (A == 6)? 1: 4, (B == 6)? 2: 5, (C == 6)? 3: 6 );

Ngoài ra, bạn có thể sử dụng toán tử bậc ba khi khởi tạo.

const int i = (A == 6)? 1 : 4;

Làm điều đó với if rất lộn xộn:

int i_temp;
if (A == 6)
   i_temp = 1;
else
   i_temp = 4;
const int i = i_temp;

Bạn không thể đặt phần khởi tạo bên trong if / else, vì nó thay đổi phạm vi. Nhưng các tham chiếu và biến const chỉ có thể bị ràng buộc khi khởi tạo.


2

Toán tử bậc ba có thể được bao gồm trong một giá trị, trong khi một if-then-else không thể; mặt khác, if-then-else có thể thực thi các vòng lặp và các câu lệnh khác, trong khi toán tử bậc ba chỉ có thể thực thi các giá trị (có thể là void).

Trên một ghi chú liên quan, dấu && và || toán tử cho phép một số mẫu thực thi khó triển khai hơn với if-then-else. Ví dụ: nếu một người có một số hàm để gọi và muốn thực thi một đoạn mã nếu bất kỳ hàm nào trong số chúng không thành công, thì nó có thể được thực hiện một cách dễ dàng bằng cách sử dụng toán tử &&. Làm điều đó mà không có toán tử đó sẽ yêu cầu mã dư thừa, một goto hoặc một biến cờ bổ sung.


1

Với C # 7 , bạn có thể sử dụng tính năng ref local mới để đơn giản hóa việc gán có điều kiện cho các biến tương thích với ref. Vì vậy, bây giờ, không chỉ bạn có thể làm:

int i = 0;

T b = default(T), c = default(T);

// initialization of C#7 'ref-local' variable using a conditional r-value⁽¹⁾

ref T a = ref (i == 0 ? ref b : ref c);

... nhưng cũng vô cùng tuyệt vời:

// assignment of l-value⁽²⁾ conditioned by C#7 'ref-locals'

(i == 0 ? ref b : ref c) = a;

Dòng mã đó chỉ định giá trị của amột trong hai bhoặc c, tùy thuộc vào giá trị của i.



Ghi chú
1. r có giá trịđúng bên -Tay chuyển nhượng, giá trị đó được giao.
2. l-giá trịtrái phía -Tay chuyển nhượng, biến mà nhận giá trị được giao.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.