Hai dòng này có giống nhau không, '? …: 'Vs' ?? '?


80

Có sự khác biệt giữa hai dòng này?

MyName = (s.MyName == null) ? string.Empty : s.MyName

hoặc là

MyName = s.MyName ?? string.Empty

8
Kiểm tra IL trong số họ, như vậy bạn sẽ có một ý tưởng tốt hơn nếu họ sẽ sản xuất cùng một mã hoặc họ đang được xử lý khác nhau
— manman

12
@manman Nếu ai đó không hiểu mã cấp cao, điều gì khiến bạn có ấn tượng rằng mã IL sẽ dễ đọc hơn ?
— Servy

11
@Servy Khi một số người tò mò về việc tìm hiểu sự khác biệt và những người khác đang bao quát các cuộc thảo luận cấp cao, bạn nên chỉ ra những cách khác để hiểu sự khác biệt và nếu anh ta rất tò mò, anh ta có thể đi và chỉ cần so sánh kết quả, không cần phải đọc toàn bộ IL
— manman

6
Sự khác biệt duy nhất là bạn đánh giá s.MyNamemột lần hay hai lần.
— Tim S.

2
@Servy bởi vì bạn không cần phải hiểu mã IL, bạn chỉ cần diffnó
— wchargin

Câu trả lời:


166

CẬP NHẬT: Tôi đã viết một bài blog thảo luận sâu hơn về chủ đề này. http://www.codeducky.org/properties-fields-and-methods-oh-my/


Nói chung chúng sẽ trả về cùng một kết quả. Tuy nhiên, có một số trường hợp bạn sẽ gặp phải sự khác biệt đáng chú ý khi MyNamelà một thuộc tính vì MyNamegetter sẽ được thực thi hai lần trong ví dụ đầu tiên và chỉ một lần trong ví dụ thứ hai.

Ví dụ: bạn có thể gặp sự khác biệt về hiệu suất khi thực hiện MyNamehai lần:

string MyName
{
    get 
    {
        Thread.Sleep(10000);
        return "HELLO";
    }
}

Hoặc bạn có thể nhận được các kết quả khác khi thực hiện MyNamehai lần nếu MyNametrạng thái là:

private bool _MyNameHasBeenRead = false;

string MyName
{
    get 
    {
        if(_MyNameHasBeenRead)
                throw new Exception("Can't read MyName twice");
        _MyNameHasBeenRead = true;
        Thread.Sleep(10000);
        return "HELLO";
    }
}

Hoặc bạn có thể nhận được kết quả khác khi thực hiện MyNamehai lần nếu MyNamecó thể được thay đổi trên một chuỗi khác:

void ChangeMyNameAsync()
{
    //MyName set to null in another thread which makes it 
    //possible for the first example to return null
    Task.Run(() => this.MyName = null);
}

string MyName { get; set; }  

Đây là cách mã thực sự được biên dịch. Đầu tiên là mảnh có biểu thức bậc ba:

IL_0007:  ldloc.0     // s
IL_0008:  callvirt    s.get_MyName       <-- first call
IL_000D:  brfalse.s   IL_0017
IL_000F:  ldloc.0     // s
IL_0010:  callvirt    s.get_MyName       <-- second call
IL_0015:  br.s        IL_001C
IL_0017:  ldsfld      System.String.Empty
IL_001C:  call        set_MyName

và đây là phần có toán tử null-kết hợp:

IL_0007:  ldloc.0     // s
IL_0008:  callvirt    s.get_MyName       <-- only call
IL_000D:  dup         
IL_000E:  brtrue.s    IL_0016
IL_0010:  pop         
IL_0011:  ldsfld      System.String.Empty
IL_0016:  call        s.set_MyName

Như bạn có thể thấy mã đã biên dịch cho toán tử bậc ba sẽ thực hiện hai lệnh gọi để nhận giá trị thuộc tính, trong khi toán tử liên kết rỗng sẽ chỉ thực hiện 1.


3
MyName = s.MyName ?? string.Emptysẽ không thực thi getter hai lần nếu MyNamelà một thuộc tính. Vì vậy, nếu bạn không muốn thực thi getter hai lần, thì bạn nên sử dụng dòng thứ hai.
— Steven Wexler

2
Tôi đã tự do thêm các ví dụ IL về hai đoạn mã. Để có ví dụ đầy đủ về LINQPad , hãy xem dropbox.com/s/x6zqdsjlkosxchf/SO21052437.linq
— Lasse V. Karlsen

1
Tôi nghĩ rằng bạn đã bỏ lỡ một "s" trên phần cuối cùng của mã IL; hiện tại nó đang đọc et_MyName, nên được set_MyName.
— AJMansfield

2
Ngoài ra, phiên bản dài với ?:thực sự có thể dẫn đến nullnhững lý do trên. Nó có thể minh họa hơn nếu "Value from other thread"ở trên thực sự là một nullchuỗi. Sau đó, điều đó sẽ cho thấy rằng kết quả có thể là rỗng. Điều tương tự cũng có thể xảy ra với đối tượng trạng thái nếu đối tượng quyết định trả về một cá thể chuỗi trong lần gọi đầu tiên của getaccessoor và trả về null chỉ trên lần gọi thứ hai.
— Jeppe Stig Nielsen

2
Cũng lưu ý rằng C # 4.0 thông số kỹ thuật ngôn ngữ đảm bảo (trong §7.14) mà myValue được thực sự đánh giá hai lần do đó ngăn ngừa các jitter để tối ưu hóa ra rằng cuộc gọi kép (như tôi nghĩ rằng nó sẽ lúc đầu)
— BlackBear

26

Nếu thuộc tính nhiều hơn một getter đơn giản, bạn có thể đang thực thi một hàm hai lần trong trường hợp khác rỗng cho cái đầu tiên.

Nếu thuộc tính nằm trong một đối tượng trạng thái, thì lệnh gọi thứ hai đến thuộc tính có thể trả về một kết quả khác:

class MyClass
{
    private IEnumerator<string> _next = Next();

    public MyClass()
    {
        this._next.MoveNext();
    }

    public string MyName
    {
        get
        {
            var n = this._next.Current;
            this._next.MoveNext();
            return n;
        }
    }


    public static IEnumerator<string> Next()
    {
        yield return "foo";
        yield return "bar";
    }
}

Ngoài ra, trong trường hợp không phải là chuỗi, lớp có thể quá tải == để thực hiện điều gì đó khác với toán tử bậc ba. Tôi không tin rằng toán tử bậc ba có thể bị quá tải.


Toán tử bậc ba chắc chắn không thể bị quá tải. Ngay cả C ++, cho phép bạn nạp chồng trên thực tế mọi toán tử khác, cũng không có cách nào để nạp chồng ?:. Tôi chưa thấy bất kỳ ngôn ngữ nào làm được.
— Darrel Hoffman

@DarrelHoffman Luôn có Scala, nơi bạn có thể tự mình lăn lộn;)
— jdphenix

9

Sự khác biệt duy nhất là bạn đánh giá s.MyNamehai lần hay một lần. Lần đầu tiên sẽ thực hiện hai lần trong trường hợp s.MyNamekhông phải là giá trị rỗng, lần thứ hai sẽ chỉ đánh giá nó một lần.

Trong hầu hết các trường hợp, sự khác biệt này không quan trọng, và tôi sẽ chọn thứ hai vì nó rõ ràng và ngắn gọn hơn.


5

Đúng, cả hai đều giống nhau và nó là toán tử kết hợp rỗng .

Nó trả về toán hạng bên trái nếu toán hạng không rỗng; nếu không nó trả về toán hạng bên phải.

Nếu chúng ta nói về hiệu quả thì

string MyName = (s.MyName == null) ? string.Empty : s.MyName;
string MyName2 = s.MyName ?? string.Empty;

Nếu tôi sử dụng bộ phổ biến thì tôi có thể thấy rằng câu lệnh đầu tiên cần 19 câu lệnh để được thực thi bởi trình biên dịch trong khi câu lệnh thứ hai chỉ yêu cầu 12 câu lệnh được thực thi.


Cảm ơn vì đã nhắc đến tên. Tôi muốn xem nếu Java cũng có cái này ?? nhà điều hành. Và nó không, vì vậy tôi sẽ gắn bó với toán tử bậc ba cho cả hai.
— developerwjk

0

Vâng, họ cũng làm như vậy. ??là cách viết tắt để kiểm tra null.


0

Họ hoàn thành cùng một nhiệm vụ.

Chỉ có sự khác biệt là khả năng đọc được liệu đồng nghiệp của bạn hoặc bất kỳ ai đang đọc mã có hiểu cú pháp hay không.

CHỈNH SỬA: Ngoài ra, tùy chọn đầu tiên có thể đánh giá tài sản MyNamehai lần.


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 cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.