Chuyển đổi bản sao kê… có nên cho phép không? [đóng cửa]


120

Miễn là tôi có thể nhớ, tôi đã tránh sử dụng thông báo chuyển đổi. Trên thực tế, tôi không thể nhớ nó đã bao giờ đi vào ý thức của tôi như một cách khả thi để làm mọi việc vì nó đã được tôi ghi nhớ từ rất sớm rằng nó chỉ là một lỗi trong câu lệnh chuyển đổi. Tuy nhiên, hôm nay tôi xem qua một số mã sử dụng nó theo thiết kế, khiến tôi ngay lập tức tự hỏi mọi người trong cộng đồng nghĩ gì về câu lệnh chuyển đổi.

Đó có phải là thứ mà một ngôn ngữ lập trình không nên cho phép một cách rõ ràng (như C #, mặc dù nó cung cấp một giải pháp thay thế) hay nó là một tính năng của bất kỳ ngôn ngữ nào đủ mạnh để nằm trong tay của lập trình viên?

Chỉnh sửa: Tôi không đủ cụ thể với những gì tôi muốn nói về việc vượt qua. Tôi sử dụng loại này rất nhiều:

    switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something
            break;
        case 2:
        case 3:
        case 4:
            // Do something
            break;
   }

Tuy nhiên, tôi lo ngại về một thứ như thế này.

   switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something, but fall through to the other cases
            // after doing it.

        case 2:
        case 3:
        case 4:
            // Do something else.
            break;
   }

Bằng cách này bất cứ khi nào trường hợp là 0, 1, nó sẽ thực hiện mọi thứ trong câu lệnh switch. Tôi đã thấy điều này theo thiết kế và tôi chỉ không biết liệu tôi có đồng ý rằng các câu lệnh switch nên được sử dụng theo cách này hay không. Tôi nghĩ ví dụ mã đầu tiên là một ví dụ rất hữu ích và an toàn. Điều thứ hai có vẻ nguy hiểm.



51
Không đồng ý về việc không mang tính xây dựng. 27 phiếu ủng hộ cho câu hỏi nói rằng có những người khác cũng cảm thấy như vậy.
Chế độ Wes

2
"không mang tính xây dựng" là một lý do gần gũi cũ và hơi mơ hồ. Ngày nay, những câu hỏi như vậy có thể và nên được gắn cờ để kết thúc là hoàn toàn dựa trên quan điểm. SO dành cho các câu hỏi có thể trả lời được về mã hoặc thiết kế, không phải 'ý kiến ​​của "cộng đồng" [bất cứ điều gì] về ý kiến ​​của tôi về Tính năng X'. Programmers.SE sẽ phù hợp hơn, nhưng ngay cả khi đó, giá trị của một câu hỏi rộng và định hướng quan điểm như vậy vẫn còn rất đáng ngờ.
underscore_d

1
Có những trường hợp sử dụng rất rõ ràng trong đó sử dụng đường dự phòng, vì vậy không chắc tại sao nó sẽ dựa trên ý kiến. Tôi đồng ý rằng câu hỏi này hơi không rõ ràng, nhưng nó không nên được đóng lại là không mang tính xây dựng. Có thể chỉ được chỉnh sửa để trở thành một trường hợp rõ ràng hơn về: "Khi nào thì sử dụng kỹ thuật dự phòng?" Mà đã có câu trả lời rõ ràng. Các văn bản C ++ tốt nói về vấn đề này, nó gây nhầm lẫn cho các lập trình viên mới (hoặc thậm chí là các lập trình viên có kinh nghiệm hơn từ các ngôn ngữ khác), vì vậy có vẻ là một câu hỏi hay cho SO. Thật vậy, nó có vẻ giống như một câu hỏi SO tốt về nguyên mẫu . Ngay cả khi cách nó được yêu cầu không hoàn hảo.
eric

Câu trả lời:


89

Nó có thể phụ thuộc vào những gì bạn cho là dự phòng. Tôi ổn với loại điều này:

switch (value)
{
  case 0:
    result = ZERO_DIGIT;
    break;

  case 1:
  case 3:
  case 5:
  case 7:
  case 9:
     result = ODD_DIGIT;
     break;

  case 2:
  case 4:
  case 6:
  case 8:
     result = EVEN_DIGIT;
     break;
}

Nhưng nếu bạn có một nhãn trường hợp theo sau là mã chuyển sang một nhãn trường hợp khác, tôi sẽ luôn coi đó là điều ác. Có lẽ chuyển mã chung sang một hàm và gọi từ cả hai nơi sẽ là một ý tưởng tốt hơn.

Và xin lưu ý rằng tôi sử dụng định nghĩa Câu hỏi thường gặp trong C ++ về "cái ác"


Tôi đồng ý với bạn: Tôi không nghĩ ví dụ của bạn là dự phòng, và nếu có trường hợp tôi muốn thực thi nhiều khối mã thông qua dự phòng, có lẽ có một cách tốt hơn để làm điều đó.
Dave DuPlantis 9/10/08

10
+1 chỉ đơn giản cho định nghĩa về "cái ác". Tôi sẽ mượn cái đó, cảm ơn ;-)
Joachim Sauer

Tôi hiểu quan điểm của bạn và bạn đúng, nhưng ví dụ của bạn thực sự tồi tệ. Không ai nên kiểm tra các chữ số thử nghiệm như thế này. Scnr, câu trả lời của bạn dù sao cũng đúng.
Tim Büthe

Trường hợp cụ thể, đó là điều C # không cho phép. Có thể vì một lý do :-)
Joey

Đáng buồn thay, định nghĩa về cái ác dường như đã bị ngoại tuyến
HopeHelpful

57

Đó là một con dao hai lưỡi. Nó đôi khi rất hữu ích, nhưng thường nguy hiểm.

Khi nào thì tốt? Khi bạn muốn 10 trường hợp được xử lý theo cùng một cách ...

switch (c) {
  case 1:
  case 2:
            ... Do some of the work ...
            /* FALLTHROUGH */
  case 17:
            ... Do something ...
            break;
  case 5:
  case 43:
            ... Do something else ...
            break;
}

Một quy tắc mà tôi thích là nếu bạn từng làm bất cứ điều gì lạ lùng mà bạn loại trừ thời gian nghỉ, bạn cần có một nhận xét rõ ràng / * FALLTHROUGH * / để cho biết đó là ý định của bạn.


3
+1! Tôi đồng ý rằng đôi khi nó là câu trả lời đúng, và tôi ĐÔI NÉT đồng ý rằng khi nó được thiết kế, nó nên được nhận xét. (Trong một bài đánh giá mã, tôi muốn làm cho nhà phát triển bình luận khác một bởi thiết kế không có sản phẩm nào vào mùa thu qua Không phải là nó sẽ xảy ra, chúng tôi là một cửa hàng # C, nơi mà không được hỗ trợ :).
John Rudy

4
Bản thân tôi sử dụng nhận xét / * FALL QUA * / trong mã của tôi khi thích hợp. =]
strager

2
Nếu bạn là PC-Lint, bạn phải chèn / * - fallthrough * /, nếu không nó sẽ phàn nàn.
Steve Melnikoff

1
Tuy nhiên, nếu bạn muốn làm rõ rằng hành vi là cố ý, bạn cũng có thể if(condition > 1 && condition <10) {condition = 1}; switch(...cứu được các trường hợp (để có cái nhìn rõ ràng) cộng với mọi người có thể thấy rằng bạn thực sự muốn những trường hợp đó kích hoạt cùng một hành động.
Đánh dấu

1
Đây vẫn là mã hóa tồi tệ. Trường hợp đầu tiên nên gọi một hàm, trường hợp thứ hai nên gọi cùng một hàm sau đó gọi hàm thứ hai.
BlueRaja - Danny Pflughoeft

21

Bạn đã nghe nói về thiết bị của Duff chưa? Đây là một ví dụ tuyệt vời về việc sử dụng dự phòng chuyển đổi.

Đó là một tính năng có thể được sử dụng và nó có thể bị lạm dụng, giống như hầu hết các tính năng ngôn ngữ.


Học điều gì đó mới mỗi ngày - cảm ơn! Tôi thực sự cảm thấy cho những nhà phát triển nghèo luôn mặc dù vậy.
Justin R.

Chà, đó là một thứ khá quấn lấy bộ não của bạn.
Fostah

6
Lưu ý nhận xét được trích dẫn của Duff trong bài báo đó: "Đoạn mã này tạo thành một số loại tranh luận trong cuộc tranh luận đó, nhưng tôi không chắc liệu nó ủng hộ hay chống lại."
John Carter

Thiết bị của Duff rõ ràng là xấu xa
Marjeta

21

Vượt qua thực sự là một điều hữu ích, tùy thuộc vào những gì bạn đang làm. Hãy xem xét cách sắp xếp các tùy chọn gọn gàng và dễ hiểu này:

switch ($someoption) {
  case 'a':
  case 'b':
  case 'c':
    // Do something
    break;

  case 'd':
  case 'e':
    // Do something else
    break;
}

Hãy tưởng tượng làm điều này với if / else. Nó sẽ là một mớ hỗn độn.


1
Tôi thấy kiểu ngụy biện này rất hữu ích
Người bán ở Mitchel

if 'do something' không chỉ là một chút so với công tắc sẽ là một mớ hỗn độn hơn if / else. Với if / else, mọi thứ đều rõ ràng là biến nào được kiểm tra. Ngay cả ví dụ nhỏ này cũng sẽ không tệ lắm với if / else trong mắt tôi
grenix

10

Nó có thể rất hữu ích trong một vài lần, nhưng nói chung, không có hành vi nào là hành vi mong muốn. Rơi qua nên được cho phép, nhưng không ngầm.

Ví dụ, để cập nhật phiên bản cũ của một số dữ liệu:

switch (version) {
    case 1:
        // Update some stuff
    case 2:
        // Update more stuff
    case 3:
        // Update even more stuff
    case 4:
        // And so on
}

6

Tôi thích một cú pháp khác cho các dự phòng trong công tắc, đại loại như, err ..

switch(myParam)
{
  case 0 or 1 or 2:
    // Do something;
    break;
  case 3 or 4:
    // Do something else;
    break;
}

Lưu ý: Điều này đã có thể thực hiện được với enum, nếu bạn khai báo tất cả các trường hợp trên enum của mình bằng cách sử dụng cờ, phải không? Nghe cũng không tệ lắm; các trường hợp có thể (nên?) rất tốt là một phần của enum của bạn rồi.

Có lẽ đây sẽ là một trường hợp tốt (không có ý định chơi chữ) cho một giao diện thông thạo sử dụng các phương pháp mở rộng? Một cái gì đó như, errr ...

int value = 10;
value.Switch()
  .Case(() => { /* Do something; */ }, new {0, 1, 2})
  .Case(() => { /* Do something else */ } new {3, 4})
  .Default(() => { /* Do the default case; */ });

Mặc dù điều đó có lẽ thậm chí còn ít đọc hơn: P


1
Tôi thích gợi ý đầu tiên rất nhiều. Điều đó đọc rất tốt và tiết kiệm một vài dòng mã. Cái thứ hai mất một phút để cuốn lấy bộ não của tôi nhưng có lý, nhưng nó trông có vẻ là một mớ hỗn độn.
Fostah

1
Vâng, đó là những gì tôi cũng nghĩ, đó chỉ là một sự cố ý ngẫu nhiên mà tôi đặt ra, trong nỗ lực tìm một cách khác để chuyển đổi bằng cách sử dụng các cấu trúc ngôn ngữ hiện có.
Erik van Brakel

Chạy qua câu hỏi này và tôi thực sự thích điều này! Có một ý tưởng về cách cải thiện khả năng đọc này nhưng SO không cho phép tôi gửi ý kiến nhiều dòng :( Có người nên thực hiện điều này cũng giống như POC, trông thú vị để thực hiện và sử dụng (:
Victor Elias

5

Như với bất cứ thứ gì: nếu được sử dụng cẩn thận, nó có thể là một công cụ tao nhã.

Tuy nhiên, tôi nghĩ rằng những hạn chế hơn là biện minh cho việc không sử dụng nó, và cuối cùng là không cho phép nó nữa (C #). Trong số các vấn đề là:

  • thật dễ dàng để "quên" thời gian nghỉ ngơi
  • Không phải lúc nào những người bảo trì mã cũng rõ ràng rằng một lỗi bị bỏ qua là cố ý

Sử dụng tốt công tắc / trường hợp dự phòng:

switch (x)
{
case 1:
case 2:
case 3:
 Do something
 break;
}

Baaaaad sử dụng một công tắc / trường hợp dự phòng:

switch (x)
{
case 1:
    Some code
case 2:
    Some more code
case 3:
    Even more code
    break;
}

Điều này có thể được viết lại bằng cách sử dụng cấu trúc if / else mà không mất mát gì theo ý kiến ​​của tôi.

Lời cuối cùng của tôi: hãy tránh xa các nhãn viết hoa như trong ví dụ xấu , trừ khi bạn đang duy trì mã kế thừa nơi phong cách này được sử dụng và hiểu rõ.


3
Bạn có thể viết lại hầu hết các cấu trúc ngôn ngữ bằng cách sử dụng if () và goto ... điều đó không biện minh cho việc bỏ một cấu trúc rõ ràng.
Shog9

2
Tôi biết nhưng cấu trúc switch / case sử dụng rõ ràng "trường hợp 1: một số trường hợp mã 2: một số trường hợp mã khác 3: ngắt mã cuối cùng;" có thể và nên được viết lại bằng if / else if (ý kiến ​​của tôi).
steffenj

1
Lưu ý rằng chuyển đổi có thể nhanh hơn nhiều lần so với danh sách các if-elses. Switch sử dụng bảng nhảy hoặc khi không thể thực hiện được, các bước nhảy có điều kiện của nó sẽ được cấu trúc trong cây quyết định thay vì một danh sách tuyến tính các bước nhảy có điều kiện. Các câu lệnh If-else lồng nhau có cấu trúc tốt, tốt nhất sẽ nhanh như nhau.
Jochem Kuijpers

4

Nó mạnh mẽ và nguy hiểm. Vấn đề lớn nhất đối với sự chuyển giao là nó không rõ ràng. Ví dụ: nếu bạn bắt gặp mã được chỉnh sửa thường xuyên có một nút chuyển với các lỗi, làm thế nào bạn biết đó là cố ý chứ không phải lỗi?

Bất kỳ nơi nào tôi sử dụng nó, tôi đảm bảo rằng nó được nhận xét đúng:

switch($var) {
    case 'first':
        // Fall-through
    case 'second':
        i++;
        break;
 }

2
Mục đích là rõ ràng nếu không có mã cho trường hợp 'đầu tiên'. Nếu bạn viết mã trong đó và cũng muốn chạy mã cho trường hợp 'thứ hai', thì nhận xét là quan trọng. Nhưng dù sao thì tôi cũng sẽ tránh được viễn cảnh đó.
Joel Coehoorn 9/10/08

1
@Joel - Tôi hoàn toàn đồng ý. Trong cửa hàng của tôi, chúng tôi đã để mọi người đưa vào bình luận trong những trường hợp như vậy, và tôi nghĩ nó làm giảm khả năng đọc. Tuy nhiên, nếu có mã ở đó, hãy đưa vào bình luận dự phòng.
Fred Larson

Chúng tôi có nghĩa là gì "không rõ ràng". Đây là lý do tại sao chúng ta phá vỡ; Các ngôn ngữ khác làm điều này. Đó chỉ là vấn đề đối với những người chưa học ngôn ngữ của họ theo thứ tự. C # yêu cầu nó cho ffs trường hợp mặc định, không có lý do gì để imho.
mckenzm

3

Sử dụng cú ngã như trong ví dụ đầu tiên của bạn rõ ràng là OK, và tôi sẽ không coi đó là một cú rơi thực sự.

Ví dụ thứ hai là nguy hiểm và (nếu không được bình luận rộng rãi) không rõ ràng. Tôi dạy học sinh của mình không sử dụng các cấu trúc như vậy trừ khi họ cho rằng việc dành một khối bình luận cho nó là xứng đáng, trong đó mô tả rằng đây là một sự cố ý và tại sao giải pháp này tốt hơn các giải pháp thay thế. Điều này không khuyến khích việc sử dụng cẩu thả, nhưng nó vẫn được phép sử dụng trong những trường hợp được sử dụng để làm lợi thế.

Điều này ít nhiều tương đương với những gì chúng tôi đã làm trong các dự án không gian khi ai đó muốn vi phạm tiêu chuẩn mã hóa: họ phải nộp đơn xin cấp phép (và tôi đã được gọi để tư vấn về phán quyết).


2

Tôi không thích các switchtuyên bố của mình bị bỏ sót - nó quá dễ xảy ra lỗi và khó đọc. Ngoại lệ duy nhất là khi nhiều casecâu lệnh cùng làm một việc chính xác .

Nếu có một số mã chung mà nhiều nhánh của câu lệnh switch muốn sử dụng, tôi trích xuất mã đó thành một hàm chung riêng có thể được gọi trong bất kỳ nhánh nào.


1

Trong một số trường hợp, việc sử dụng các bước chuyển tiếp là một hành động lười biếng của lập trình viên - họ có thể sử dụng một loạt || chẳng hạn như câu lệnh, nhưng thay vào đó sử dụng một loạt các trường hợp chuyển đổi 'bắt tất cả'.

Điều đó đang được nói, tôi thấy chúng đặc biệt hữu ích khi tôi biết rằng cuối cùng tôi vẫn cần các tùy chọn (ví dụ: trong một phản hồi menu), nhưng vẫn chưa thực hiện tất cả các lựa chọn. Tương tự như vậy, nếu bạn đang thực hiện chuyển đổi cho cả 'a' và 'A', tôi thấy việc sử dụng chuyển đổi chuyển đổi về cơ bản rõ ràng hơn là lệnh if ghép.

Đó có thể là vấn đề về phong cách và cách các lập trình viên suy nghĩ, nhưng tôi thường không thích loại bỏ các thành phần của một ngôn ngữ dưới danh nghĩa 'an toàn' - đó là lý do tại sao tôi hướng tới C và các biến thể / hậu duệ của nó hơn là, Java. Tôi thích có thể tìm hiểu xung quanh với các con trỏ và những thứ tương tự, ngay cả khi tôi không có "lý do" để làm.


Nói đúng. Các câu lệnh switch nhảy qua một giá trị đã đặt đến một loạt các vị trí mã cụ thể. Mặc dù trình biên dịch có khả năng nắm bắt những thứ như vậy nhưng có một giá trị tốc độ và độ đúng đối với một câu lệnh switch trên một chuỗi hoặc các câu lệnh. Nếu p là 0 hoặc p là 1 hoặc p là 2. Tôi thực sự phải đánh giá xem p có phải là 0 và p là 1 hay không, thay vì nhảy đến vị trí p = 2 trong mã. Điều này đã xảy ra giống hệt với p0 và p1. Nhưng, tôi chưa bao giờ phải kiểm tra chúng.
Tatarize

1

Fall-through chỉ nên được sử dụng khi nó được sử dụng như một bảng nhảy vào một khối mã. Nếu có bất kỳ phần nào của mã có dấu ngắt không điều kiện trước nhiều trường hợp hơn, tất cả các nhóm trường hợp sẽ kết thúc theo cách đó.

Còn gì nữa là "ác".

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.