Khởi tạo các đối tượng Null bằng toán tử Null-Coalescing


12

Hãy xem xét kịch bản điển hình sau:

if(myObject == null) {
    myObject = new myClass();
}

Tôi đang tự hỏi người ta nghĩ gì về sự thay thế sau bằng cách sử dụng toán tử hợp nhất null:

myObject = myObject ?? new myClass();

Tôi không chắc liệu tôi có nên sử dụng mẫu thứ hai không. Nó có vẻ như một tốc ký tốt đẹp, nhưng myObject = myObjectcấu trúc lúc đầu có vẻ như có thể là một chút mùi mã.

Đây có phải là một điều hợp lý để làm, hoặc có một tốc ký tốt hơn mà tôi đang thiếu? Hoặc có thể, "Đó là ba dòng, vượt qua nó!"?

Chỉnh sửa: Như đã được đề cập, có lẽ gọi đây là một kịch bản điển hình là một cái gì đó quá lời. Tôi thường thấy rằng tôi gặp phải tình huống này khi tôi truy xuất một thực thể từ cơ sở dữ liệu có thuộc tính loại tham chiếu con có thể hoặc không thể được tạo ra:

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();

15
Người mới tin rằng những thứ như thế này là "hackish", các nhà phát triển có kinh nghiệm hơn chỉ gọi nó là "thành ngữ".
Doc Brown

Nếu đối tượng trông giống như một đối tượng giá trị ("có ngữ nghĩa giá trị", theo cách nói thành ngữ), MyClassnên cung cấp một public static readonlythành viên của một Emptygiá trị thuộc loại. Xem String.Emptytrên MSDN .
rwong


5
Không có ??=nhà điều hành?
aviv

1
@aviv Mình ước có!
Loren Pechtel

Câu trả lời:


16

Tôi sử dụng toán tử hợp nhất null mọi lúc. Tôi thích sự đồng nhất của nó.

Tôi thấy toán tử này có bản chất tương tự toán tử ternary (A? B: C). Phải thực hành một chút trước khi đọc nó là bản chất thứ hai, nhưng một khi bạn đã quen với nó, tôi cảm thấy khả năng đọc được cải thiện so với các phiên bản tay dài.

Ngoài ra, tình huống bạn mô tả chỉ là một kịch bản mà toán tử hữu ích. Nó cũng thuận tiện để thay thế các cấu trúc như thế này:

if (value != null)
{
    return value;
}
else
{ 
    return otherValue;
}

hoặc là

return value != null ? value : otherValue;

với

return value ?? otherValue;

Chắc chắn đồng ý chung về việc sử dụng toán tử này. Tuy nhiên, khi tôi thấy các tài liệu tham khảo về việc sử dụng nó, nó thường giống như thế myObject = myObject2 ?? myObject3. Điều tôi băn khoăn cụ thể là sử dụng toán tử để đặt đối tượng thành chính nó trừ khi nó không có giá trị.
grin0048

2
Tôi nghĩ rằng lý do tương tự áp dụng trong cả hai trường hợp. Đó là một cách ngắn gọn hơn để diễn đạt cùng một logic.
17 trên 26 tháng

Tôi nhớ đã nhìn vào IL cho mỗi trong ba hình thức đó một lần. Cái thứ nhất và cái thứ hai giống hệt nhau, nhưng cái thứ ba được tối ưu hóa theo cách mà nó có thể giả định nulllà so sánh.
Jesse C. Choper

2

Điều tôi băn khoăn cụ thể là sử dụng toán tử để đặt một đối tượng thành chính nó trừ khi nó không có giá trị.

Các ?? operator được gọi là các nhà điều hành null-coalescing và được sử dụng để xác định một giá trị mặc định cho các loại giá trị nullable hoặc các loại tài liệu tham khảo. 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.

myObject = myObject ?? new myObject(); - instantiate default value of object

Thêm chi tiết và mẫu mã về toán tử hợp nhất null - bài viết MSDN .

Nếu bạn kiểm tra more than nullableđiều kiện, như là một thay thế bạn có thể sử dụng toán tử Ternary.

Toán tử ternary

Bạn cũng có thể nhìn vào ?: Nhà điều hành . Nó được gọi là Ternary hoặc toán tử có điều kiện . Toán tử điều kiện (? :) trả về một trong hai giá trị tùy thuộc vào giá trị của biểu thức Boolean.

Một loại nullable có thể chứa một giá trị, hoặc nó có thể không được xác định. Các ?? toán tử xác định giá trị mặc định được trả về khi loại nullable được gán cho loại không nullable. Nếu bạn cố gắng gán loại giá trị nullable cho loại giá trị không nullable mà không sử dụng ?? toán tử, bạn sẽ tạo ra một lỗi thời gian biên dịch. Nếu bạn sử dụng một kiểu truyền và loại giá trị nullable hiện không được xác định, một ngoại lệ UnlimitedOperationException sẽ bị ném.

Một ví dụ mã từ MSDN - ?: Toán tử (Tham chiếu C #) :

int? input = Convert.ToInt32(Console.ReadLine());
string classify;

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";

Vì vậy, lấy ví dụ từ câu hỏi và áp dụng toán tử điều kiện, chúng ta sẽ có một cái gì đó như thế myObject = (myObject == null) ? new myClass() : myObject. Vì vậy, trừ khi tôi thiếu sử dụng toán tử có điều kiện tốt hơn, nó dường như có cùng một "vấn đề" với việc đặt một đối tượng thành chính nó (nếu nó không rỗng) như là toán tử hợp nhất null - và nó ít súc tích hơn.
grin0048

Nếu bạn đang kiểm tra nhiều hơn một điều kiện (Không null và lớn hơn 0) - thì toán tử có điều kiện sẽ thực hiện. Tuy nhiên, chỉ cần kiểm tra null sẽ làm việc cho ?? nhà điều hành.
Yusubov

2

Tôi không nghĩ rằng kịch bản đó là (hoặc ít nhất nên là) điển hình.

Nếu bạn không muốn giá trị mặc định của một số trường null, thì hãy đặt nó trong trình khởi tạo trường:

myClass myObject = new myClass();

Hoặc, nếu việc khởi tạo phức tạp hơn, hãy đặt nó trong hàm tạo.

Nếu bạn chỉ muốn tạo myClasskhi bạn thực sự cần nó (ví dụ: vì việc tạo nó mất nhiều thời gian), thì bạn có thể sử dụng Lazy<T>:

Lazy<myClass> myObject = new Lazy<myClass>();

(Điều này gọi hàm tạo mặc định. Nếu quá trình khởi tạo phức tạp hơn, hãy chuyển lambda tạo myClasscho hàm Lazy<T>tạo.)

Để truy cập giá trị, hãy sử dụng myObject.Value, sẽ gọi khởi tạo nếu đây là lần đầu tiên bạn truy cập myObject.Value.


Tạo lười biếng IMO chỉ hữu ích nếu bạn không được đảm bảo cần đối tượng kết quả, chỉ có thời gian tôi sử dụng tạo lười biếng là khi tôi có kết quả rút tiền mà tôi đặt lại khi có gì đó thay đổi và tôi biết rằng tôi sẽ cần kết quả tương tự và recalc là đắt tiền. khôn ngoan khác Tôi chỉ không muốn làm phiền với kiểm tra null
ratchet freak

@ratchetfreak Có, đó là lý do tại sao tôi đề xuất trình khởi tạo trường trước tiên.
Svick

Có những trường hợp khác cũng có. Điều tôi đang nhìn vào lúc này: Vượt qua đầu tiên đặt ra các trường hợp ngoại lệ. Pass thứ hai đặt mặc định cho mọi thứ khác. ?? = sẽ cắt một nửa.
Loren Pechtel

1

Tôi đặc biệt thích sử dụng ??kết hợp với các tham số phương thức tùy chọn. Tôi hạn chế nhu cầu quá tải và đảm bảo điều đó có giá trị.

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
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.