Tại sao có cả biến thể ngắn mạch HOẶC cũng như biến thể không được lưu hành của toán tử đó trong C #?


9

Theo định kỳ, tôi tự hỏi về điều này:

Mạch ngắn HOẶC sẽ luôn trả về cùng giá trị mà toán tử OR không được lưu hành sẽ làm gì?

Tôi hy vọng rằng ngắn mạch HOẶC sẽ luôn luôn đánh giá nhanh hơn. Vì vậy, toán tử OR không được lưu hành có được bao gồm trong ngôn ngữ C # cho tính nhất quán không?

Tôi đã bỏ lỡ những gì?


9
Giá trị trả lại giống nhau - có. Tác dụng phụ tương tự - không.
Công việc

2
Giả sử f()tăng một ngoại lệ, xem xét true || f()true | f(). Bạn có thấy sự khác biệt? Biểu thức trước đánh giá true, đánh giá kết quả sau trong một ngoại lệ được đưa ra.
Scarfridge

Câu trả lời:


25

Cả hai toán hạng đều dành cho những thứ khác nhau, đến từ C không có kiểu boolean. Phiên bản ngắn mạch | | chỉ hoạt động với booleans, trong khi phiên bản không ngắn mạch | làm việc với các loại tích phân, thực hiện một bitwise hoặc. Nó chỉ tình cờ hoạt động như một hoạt động logic không ngắn mạch cho các booleans được biểu diễn bằng một bit đơn, là 0 hoặc 1.

http://en.wikibooks.org/wiki/C_Sharp_Programming/Operators#Logical


10
+1 cho liên kết. Khi tôi đọc câu hỏi này, tôi giống như "cái gì?" kể từ afaik, tên chính thức của chúng là hợp lý và bitwise hoặc, không phải là ngắn mạch và không mạch mà xảy ra là tác dụng phụ của cách chúng hoạt động.
stijn

1
+1 Tôi thấy "chỉ hoạt động trên các toán hạng boolean" - Tôi có thể không nhận thấy sự khác biệt vì dù sao tôi chỉ sử dụng các biểu thức đánh giá Boolean
CarneyCode

2
Tôi đã phải kiểm tra, nhưng thực tế là |toán tử, khi được áp dụng cho hai giá trị boolean, được biên dịch vào cùng một ortoán tử trong CIL giống như khi áp dụng cho hai giá trị nguyên - không giống như ||được biên dịch vào CIL sử dụng brtruecho điều kiện nhảy.
quentin-starin

13

|(bitwise-hoặc) phải không ngắn mạch đối với các loại như int. Đó là bởi vì trong hầu hết các trường hợp, bạn cần tính cả hai mặt của |biểu thức để tính kết quả chính xác. Ví dụ, kết quả của là 7 | f(x)gì?

Nếu f(x)không được đánh giá, bạn không thể nói.

Hơn nữa, nó sẽ không nhất quán để làm cho toán tử này ngắn mạch bool, khi nó không phải là ngắn mạch cho int. Nhân tiện, tôi nghĩ rằng tôi chưa bao giờ sử dụng |để so sánh logic một cách có chủ ý, vì thấy nó rất bất tiện khi nói |như một toán tử logic.

|| tuy nhiên là để so sánh logic, trong đó đánh giá ngắn mạch hoạt động tốt.

Điều tương tự cũng hợp lệ ngay cả đối với C và C ++, nơi nguồn gốc của các toán tử đó.


5

Điều này là chính xác, toán tử OR ngắn mạch (||) sẽ luôn trả về cùng giá trị với toán tử OR không ngắn mạch (|). (*)

Tuy nhiên, nếu toán hạng thứ nhất là đúng, toán tử ngắn mạch sẽ không đánh giá toán hạng thứ hai, trong khi toán tử không ngắn mạch sẽ luôn luôn đánh giá cả hai toán hạng. Điều này có thể có tác động đến hiệu suất, và đôi khi trong các tác dụng phụ.

Vì vậy, có một cách sử dụng cho cả hai: nếu bạn quan tâm đến hiệu suất và việc đánh giá toán hạng thứ hai không tạo ra bất kỳ tác dụng phụ nào, (hoặc nếu bạn không quan tâm đến chúng,) thì bằng mọi cách hãy sử dụng toán tử ngắn mạch . Nhưng nếu vì lý do nào đó bạn cần các tác dụng phụ của toán hạng thứ hai, thì bạn nên sử dụng toán tử không ngắn mạch.

Một ví dụ mà bạn nên sử dụng toán tử không ngắn mạch:

if( write_customer_to_database() != SUCCESS |
    write_supplier_to_database() != SUCCESS |
    write_order_to_database() != SUCCESS )
{
    transaction_rollback();
}

(*) Ngoại trừ một số tình huống thực sự sai lầm trong đó việc đánh giá toán hạng thứ nhất thành nguyên nhân sai do tác dụng phụ của toán hạng thứ hai để đánh giá thành đúng thay vì sai.


2
+1 Nhưng tôi muốn thêm rằng ngoài việc sử dụng các chức năng để biểu thị thành công hay thất bại (như trong ví dụ của bạn): thường nên tránh các chức năng có tác dụng phụ ...
Marjan Venema

1
Vâng, đó có lẽ là lý do tại sao tôi chưa bao giờ sử dụng bất kỳ nhà khai thác không ngắn mạch nào cho đến ngày hôm nay, và cơ hội là rất nhỏ mà tôi sẽ làm.
Mike Nakis

Ví dụ đó phụ thuộc vào các toán hạng được đánh giá theo thứ tự từ trái sang phải nghiêm ngặt. C # không đảm bảo điều này, nhưng nhiều ngôn ngữ tương tự (bao gồm C và C ++) thì không.
Keith Thompson

Việc đánh giá ngắn mạch sẽ không có ý nghĩa đối với ví dụ cụ thể đó, vì tất cả các giao dịch sẽ được khôi phục nếu bất kỳ giao dịch nào bị lỗi? (Tôi đang đưa ra một số giả định về ngữ nghĩa của các cuộc gọi.)
Keith Thompson

@KeithThndry bạn nói đúng, đánh giá không ngắn mạch sẽ gây ra xử lý không cần thiết và theo nghĩa này, ví dụ này hơi khập khiễng, nhưng nó không quá khập khiễng để thay đổi, bởi vì sự thật vẫn là với đánh giá ngắn mạch nếu write_customer_to_database () thành công, phần còn lại của các hàm write _... sẽ không bao giờ được gọi và tốt hơn là hoạt động chính xác trong trường hợp thành công, hơn là thực hiện một số thao tác không cần thiết trong trường hợp thất bại.
Mike Nakis

4

Sẽ có 2 lý do để sử dụng biến thể không ngắn:

  1. giữ các tác dụng phụ (nhưng rõ ràng hơn để mã với một biến tạm thời)

  2. ngăn chặn các cuộc tấn công thời gian bằng cách tránh ngã ba trong ngắn mạch (điều này thậm chí có thể cải thiện hiệu quả thời gian chạy nếu toán hạng thứ hai là một biến nhưng đó là tối ưu hóa vi mô mà trình biên dịch có thể thực hiện bằng mọi cách)


Đây là lý do đúng duy nhất được cung cấp, tôi không biết tại sao đây không phải là câu trả lời được chấp nhận với số phiếu cao nhất ...
Behrooz

2

nếu mệnh đề bên phải của toán tử có tác dụng phụ và lập trình viên dự định rằng anh ta muốn cả hai hiệu ứng phụ xảy ra trước khi kiểm tra giá trị trả về của chúng.


5
Eek. Vui lòng không viết mã như thế nếu bạn dựa vào các tác dụng phụ, có hai biểu thức khác nhau, gán kết quả và kiểm tra chúng sau đó .
Konrad Rudolph
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.