Các hàm nên trả về null hoặc một đối tượng trống?


209

Là gì thực hành tốt nhất khi trở về dữ liệu từ các chức năng. Nó là tốt hơn để trả lại một Null hoặc một đối tượng trống? Và tại sao người ta phải làm cái này hơn cái kia?

Xem xét điều này:

public UserEntity GetUserById(Guid userId)
{
     //Imagine some code here to access database.....

     //Check if data was returned and return a null if none found
     if (!DataExists)
        return null; 
        //Should I be doing this here instead? 
        //return new UserEntity();  
     else
        return existingUserEntity;
}

Hãy giả vờ rằng sẽ có trường hợp hợp lệ trong chương trình này rằng sẽ không có thông tin người dùng trong cơ sở dữ liệu với GUID đó. Tôi sẽ tưởng tượng rằng nó sẽ không thích hợp để ném một ngoại lệ trong trường hợp này ?? Ngoài ra tôi có ấn tượng rằng xử lý ngoại lệ có thể làm giảm hiệu suất.


4
Tôi nghĩ bạn có ý nghĩa if (!DataExists).
Sarah Vessels

107
Đây là một câu hỏi kiến ​​trúc và là hoàn toàn phù hợp. Câu hỏi của OP là hợp lệ bất kể vấn đề kinh doanh mà nó đang cố gắng giải quyết.
Joseph Ferris

2
Câu hỏi này đã được trả lời đầy đủ. Tôi nghĩ rằng đây là một câu hỏi rất thú vị.
John Scipione

'getUser ()' sẽ trả về null. 'getCienUserInfo ()' hoặc 'getCienPermissions ()', OTOH, sẽ tiết lộ nhiều câu hỏi hơn - họ nên trả lại một đối tượng trả lời không null bất kể ai / hoặc bất kỳ ai đăng nhập.
Thomas W

2
Không có @Bergi cái khác là một bản sao. Của tôi đã được hỏi đầu tiên, vào tháng Mười, người kia đã được hỏi 3 con bướm đêm sau đó vào tháng Mười Hai. Thêm vào đó, người khác nói về một bộ sưu tập hơi khác nhau.
7wp

Câu trả lời:


207

Trả về null thường là ý tưởng tốt nhất nếu bạn có ý định chỉ ra rằng không có dữ liệu nào khả dụng.

Một đối tượng trống ngụ ý dữ liệu đã được trả về, trong khi trả về null chỉ rõ rằng không có gì được trả về.

Ngoài ra, trả về null sẽ dẫn đến ngoại lệ null nếu bạn cố truy cập các thành viên trong đối tượng, điều này có thể hữu ích để làm nổi bật mã lỗi - cố gắng truy cập vào một thành viên không có ý nghĩa gì. Truy cập các thành viên của một đối tượng trống sẽ không thất bại có nghĩa là lỗi có thể không được phát hiện.


21
Bạn nên ném một ngoại lệ, không nuốt vấn đề và trả về null. Tối thiểu, bạn nên đăng nhập này và sau đó tiếp tục.
Chris Ballance

130
@Chris: Tôi không đồng ý. Nếu mã rõ ràng chứng minh rằng giá trị trả về là null, thì hoàn toàn có thể chấp nhận trả về null nếu không tìm thấy kết quả nào phù hợp với tiêu chí của bạn. Ném một ngoại lệ nên là lựa chọn MỚI NHẤT của bạn, không phải là LẦN ĐẦU TIÊN của bạn.
Mike Hofer

12
@Chris: Trên cơ sở nào bạn quyết định điều này? Thêm đăng nhập vào phương trình chắc chắn có vẻ quá mức. Hãy để mã tiêu thụ quyết định những gì - nếu có bất cứ điều gì - nên được thực hiện trong trường hợp không có người dùng. Như với nhận xét trước đây của tôi, hoàn toàn không có vấn đề gì với việc trả về một giá trị được định nghĩa chung là "không có dữ liệu".
Adam Robinson

17
Tôi hơi bối rối khi một nhà phát triển của Microsoft tin rằng "trả về null" tương đương với "nuốt vấn đề". Nếu bộ nhớ phục vụ, có rất nhiều phương thức trong Khung nơi các phương thức null được trả về nếu không có gì phù hợp với yêu cầu của người gọi. Có phải đó là "nuốt vấn đề?"
Mike Hofer

5
Cuối cùng nhưng không kém phần quan trọng sẽ có bool GetUserById(Guid userId, out UserEntity result)- mà tôi thích giá trị trả về "null" và không cực kỳ giống như ném một ngoại lệ. Nó cho phép nullmã đẹp miễn phí như thế nào if(GetUserById(x,u)) { ... }.
Marcel Jackwerth

44

Nó phụ thuộc vào những gì có ý nghĩa nhất cho trường hợp của bạn.

Liệu nó có ý nghĩa để trả về null, ví dụ "không có người dùng như vậy tồn tại"?

Hoặc nó có ý nghĩa để tạo một người dùng mặc định? Điều này có ý nghĩa nhất khi bạn có thể giả định một cách an toàn rằng nếu người dùng KHÔNG tồn tại, mã cuộc gọi dự định sẽ tồn tại khi họ yêu cầu.

Hoặc có nghĩa là ném một ngoại lệ (la "FileNotFound") nếu mã cuộc gọi yêu cầu người dùng có ID không hợp lệ?

Tuy nhiên - từ sự tách biệt mối quan tâm / quan điểm SRP, hai cái đầu tiên thì đúng hơn. Và về mặt kỹ thuật, điều đầu tiên là đúng nhất (nhưng chỉ bằng một sợi tóc) - GetUserById chỉ nên chịu trách nhiệm cho một điều - có được người dùng. Xử lý trường hợp "người dùng không tồn tại" của chính nó bằng cách trả lại một cái gì đó khác có thể là vi phạm SRP. Tách thành một kiểm tra khác - bool DoesUserExist(id)sẽ phù hợp nếu bạn chọn ném ngoại lệ.

Dựa trên các nhận xét mở rộng bên dưới : nếu đây là câu hỏi thiết kế cấp API, phương pháp này có thể tương tự như "OpenFile" hoặc "ReadEntireFile". Chúng tôi đang "mở" một người dùng từ một số kho lưu trữ và hydrat hóa đối tượng từ dữ liệu kết quả. Một ngoại lệ có thể thích hợp trong trường hợp này. Nó có thể không, nhưng nó có thể.

Tất cả các cách tiếp cận đều được chấp nhận - nó chỉ phụ thuộc, dựa trên bối cảnh lớn hơn của API / ứng dụng.


Ai đó đã bỏ phiếu cho bạn và tôi đã bỏ phiếu cho bạn, vì đây dường như không phải là một câu trả lời tồi cho tôi; ngoại trừ: Tôi sẽ không bao giờ đưa ra một ngoại lệ khi không tìm thấy người dùng nào trong một phương thức như phương thức mà người đăng đưa ra. Nếu việc tìm kiếm không có người dùng nào ám chỉ ID không hợp lệ hoặc một số vấn đề ngoại lệ như vậy, thì điều đó sẽ xảy ra cao hơn - phương pháp ném cần biết thêm về việc ID đó đến từ đâu, v.v.
Jacob Mattison

(Tôi đoán rằng downvote là một sự phản đối ý tưởng ném ngoại lệ trong một tình huống như thế này.)
Jacob Mattison

1
Đồng ý cho đến điểm cuối cùng của bạn. Không có vi phạm SRP bằng cách trả về một giá trị được định nghĩa chung là "không có dữ liệu". Điều đó giống như nói rằng cơ sở dữ liệu SQL sẽ trả về lỗi nếu mệnh đề where không tạo ra kết quả. Mặc dù Ngoại lệ là một lựa chọn thiết kế hợp lệ (mặc dù một lựa chọn sẽ gây khó chịu cho tôi với tư cách là người tiêu dùng), nhưng đó không phải là "chính xác" hơn là trả về null. Và không, tôi không phải là DV.
Adam Robinson

@JacobM chúng tôi đưa ra các ngoại lệ khi chúng tôi yêu cầu một đường dẫn hệ thống tập tin không tồn tại, không trả về null, nhưng không phải từ cơ sở dữ liệu. Vì vậy, rõ ràng cả hai đều phù hợp, đó là những gì tôi đang nhận được - nó chỉ phụ thuộc.
Rex M

2
@Charles: Bạn đang trả lời câu hỏi "có nên ném ngoại lệ vào một lúc nào đó không", nhưng câu hỏi là "hàm này có nên ném ngoại lệ không". Câu trả lời đúng là "có thể", không phải "có".
Adam Robinson

30

Cá nhân, tôi sử dụng NULL. Nó làm rõ rằng không có dữ liệu để trả về. Nhưng có những trường hợp khi một đối tượng Null có thể hữu ích.


Chỉ cần thêm điều này như là một câu trả lời cho mình. NullObjectPotype hoặc mẫu trường hợp đặc biệt. Sau đó, bạn có thể triển khai một cho mỗi trường hợp, NoUserEntitiesFound, NullUserEntities, v.v.
David Swindells

27

Nếu kiểu trả về của bạn là một mảng thì trả về một mảng trống nếu không trả về null.


Có 0 mục trong danh sách giống như danh sách chưa được gán tại thời điểm này không?
AnthonyWJones

3
0 mục trong danh sách không giống như null. Nó cho phép bạn sử dụng nó trong các foreachcâu lệnh và truy vấn linq mà không phải lo lắng NullReferenceException.
Darin Dimitrov

5
Tôi ngạc nhiên khi điều này đã không được nâng cao hơn nữa. Đây có vẻ là một hướng dẫn khá hợp lý với tôi.
shashi

Chà, container rỗng chỉ là một ví dụ cụ thể của mẫu đối tượng null. Mà có thể phù hợp, chúng tôi không thể nói.
Ded repeatator

Trả về một mảng trống khi dữ liệu không có sẵn chỉ đơn giản là sai . Có một sự khác biệt giữa dữ liệu có sẵn và không chứa mục nào và dữ liệu không có sẵn. Trả về một mảng trống trong cả hai trường hợp làm cho không thể biết đó là trường hợp nào. Làm điều đó chỉ để bạn có thể sử dụng một foreach mà không kiểm tra xem dữ liệu thậm chí còn tồn tại có ngớ ngẩn hay không - người gọi phải kiểm tra xem dữ liệu có tồn tại hay không và NullReferenceException nếu người gọi không kiểm tra có tốt không vì nó lộ ra lỗi ..
Phục hồi lại

12

Bạn nên ném một ngoại lệ (chỉ) nếu một hợp đồng cụ thể bị phá vỡ.
Trong ví dụ cụ thể của bạn, yêu cầu UserEntity dựa trên Id đã biết, điều này sẽ phụ thuộc vào thực tế nếu người dùng bị thiếu (bị xóa) là một trường hợp dự kiến. Nếu vậy, sau đó trở lại nullnhưng nếu nó không phải là một trường hợp dự kiến ​​thì ném một ngoại lệ.
Lưu ý rằng nếu hàm được gọi, UserEntity GetUserByName(string name)nó có thể sẽ không ném nhưng trả về null. Trong cả hai trường hợp, việc trả lại một UserEntity trống sẽ không có ích.

Đối với chuỗi, mảng và bộ sưu tập, tình huống thường khác nhau. Tôi nhớ một số mẫu MS hướng dẫn mà các phương thức nên chấp nhận nulldưới dạng danh sách 'trống' nhưng trả về các bộ sưu tập có độ dài bằng 0 thay vì null. Tương tự cho chuỗi. Lưu ý rằng bạn có thể khai báo mảng trống:int[] arr = new int[0];


Rất vui khi bạn đề cập rằng các chuỗi khác nhau, vì Google đã cho tôi thấy điều này khi tôi quyết định có trả lại một chuỗi trống hay không.
Noumenon

Chuỗi, bộ sưu tập và mảng không khác nhau. Nếu MS nói vậy thì MS sai. Có một sự khác biệt giữa một chuỗi rỗng và null và giữa một bộ sưu tập trống và null. Trong cả hai trường hợp, cái trước biểu thị dữ liệu hiện có (có kích thước 0) và cái sau đại diện cho việc thiếu dữ liệu. Trong một số trường hợp, sự phân biệt là rất quan trọng. Ví dụ: nếu bạn tìm kiếm một mục trong bộ đệm, bạn muốn biết sự khác biệt giữa dữ liệu được lưu trong bộ nhớ cache nhưng trống và dữ liệu không được lưu vào bộ đệm để bạn phải tìm nạp nó từ nguồn dữ liệu cơ bản, nơi nó có thể không trống.
Phục hồi Monica

1
Bạn dường như bỏ lỡ điểm và bối cảnh. .Wher(p => p.Lastname == "qwerty")nên trả lại một bộ sưu tập trống, không null.
Henk Holterman

@HenkHolterman Nếu bạn có thể truy cập vào bộ sưu tập đầy đủ và áp dụng bộ lọc chấp nhận không có mục nào trong bộ sưu tập, một bộ sưu tập trống là kết quả chính xác. Nhưng nếu bộ sưu tập đầy đủ không tồn tại, một bộ sưu tập trống là cực kỳ sai lệch - null hoặc ném sẽ chính xác tùy thuộc vào tình huống là bình thường hay ngoại lệ. Vì bài đăng của bạn không đủ điều kiện bạn đang nói về tình huống nào (và bây giờ bạn làm rõ bạn đang nói về tình huống trước) và vì OP đang nói về tình huống sau, tôi phải không đồng ý với bạn.
Phục hồi lại

11

Đây là một câu hỏi kinh doanh, tùy thuộc vào sự tồn tại của người dùng với Id Guid cụ thể có phải là trường hợp sử dụng bình thường dự kiến ​​cho chức năng này hay không, có phải là bất thường sẽ ngăn ứng dụng hoàn thành thành công bất kỳ chức năng nào mà phương thức này cung cấp cho người dùng chủ đề...

Nếu đó là một "ngoại lệ", thì việc không có người dùng có Id đó sẽ ngăn ứng dụng hoàn thành thành công bất kỳ chức năng nào mà nó đang thực hiện, (Giả sử chúng tôi đang tạo hóa đơn cho một khách hàng chúng tôi đã chuyển sản phẩm tới ... ), sau đó tình huống này sẽ đưa ra một ArgumentException (hoặc một số ngoại lệ tùy chỉnh khác).

Nếu một người dùng bị mất là ok, (một trong những kết quả bình thường tiềm năng của việc gọi hàm này) thì trả về null ....

EDIT: (để giải quyết nhận xét từ Adam trong một câu trả lời khác)

Nếu ứng dụng chứa nhiều quy trình nghiệp vụ, một hoặc nhiều trong số đó yêu cầu Người dùng hoàn thành thành công và một hoặc nhiều trong số đó có thể hoàn thành thành công mà không cần người dùng, thì ngoại lệ sẽ được ném lên ngăn xếp cuộc gọi, gần hơn với nơi các quy trình nghiệp vụ yêu cầu Người dùng đang gọi chuỗi thực thi này. Các phương thức giữa phương thức này và điểm đó (nơi ném ngoại lệ) chỉ nên truyền đạt rằng không có người dùng nào tồn tại (null, boolean, bất cứ điều gì - đây là một chi tiết triển khai).

Nhưng nếu tất cả các quy trình trong ứng dụng yêu cầu người dùng, tôi vẫn sẽ đưa ra ngoại lệ trong phương thức này ...


-1 cho người xuống, +1 cho Charles - đó hoàn toàn là một câu hỏi kinh doanh và không có cách nào tốt nhất cho việc này.
Austin Salonen

Nó đang băng qua suối. Có hay không một "điều kiện lỗi" được điều khiển bởi logic nghiệp vụ. Làm thế nào để xử lý đó là một quyết định kiến ​​trúc ứng dụng. Logic kinh doanh sẽ không cho rằng null được trả về, chỉ là các yêu cầu được thỏa mãn. Nếu doanh nghiệp đang quyết định các loại trả về phương thức, thì họ quá tham gia vào khía cạnh kỹ thuật của việc thực hiện.
Joseph Ferris

@joseph, nguyên tắc cốt lõi đằng sau "xử lý ngoại lệ có cấu trúc" là các ngoại lệ nên được ném khi các phương thức không thể hoàn thành bất kỳ chức năng nào mà chúng được mã hóa để thực hiện. Bạn đã đúng, nếu chức năng kinh doanh mà phương thức này đã được mã hóa để thực hiện có thể được "hoàn thành thành công", (bất kể điều đó có nghĩa là gì trong Mô hình miền), thì bạn không cần phải ném ngoại lệ, bạn có thể trả về null hoặc biến "FoundUser" boolean hoặc bất cứ điều gì ... Cách bạn giao tiếp với phương thức gọi mà không có người dùng nào được tìm thấy sau đó trở thành một chi tiết triển khai kỹ thuật.
Charles Bretana

10

Cá nhân tôi sẽ trả về null, vì đó là cách tôi mong đợi lớp DAL / Kho lưu trữ hoạt động.

Nếu nó không tồn tại, đừng trả lại bất cứ thứ gì có thể được hiểu là tìm nạp thành công một đối tượng, nullhoạt động rất đẹp ở đây.

Điều quan trọng nhất là phải nhất quán trong Lớp DAL / Repos của bạn, theo cách đó bạn không bị nhầm lẫn về cách sử dụng nó.


7

Tôi có xu hướng

  • return nullnếu id đối tượng không tồn tại khi nó không được biết trước liệu nó tồn tại hay không.
  • thrownếu đối tượng id không tồn tại khi nó nên tồn tại.

Tôi phân biệt hai kịch bản này với ba loại phương pháp. Đầu tiên:

Boolean TryGetSomeObjectById(Int32 id, out SomeObject o)
{
    if (InternalIdExists(id))
    {
        o = InternalGetSomeObject(id);

        return true;
    }
    else
    {
        return false;
    }
}

Thứ hai:

SomeObject FindSomeObjectById(Int32 id)
{
    SomeObject o;

    return TryGetObjectById(id, out o) ? o : null;
}

Ngày thứ ba:

SomeObject GetSomeObjectById(Int32 id)
{
    SomeObject o;

    if (!TryGetObjectById(id, out o))
    {
        throw new SomeAppropriateException();
    }

    return o;
}

Ý bạn là outkhôngref
Matt Ellen

@Matt: Vâng, thưa ngài, tôi chắc chắn làm được! Đã sửa.
Johann Gerell

2
Thực sự, đây là câu trả lời duy nhất phù hợp với tất cả, và do đó là sự thật hoàn toàn và duy nhất! :) Vâng, nó phụ thuộc vào các giả định dựa trên phương thức nào được gọi ... Vì vậy, trước tiên hãy làm rõ các giả định này và sau đó chọn kết hợp đúng của các phương pháp trên. Phải cuộn quá nhiều để đến đây :) +100
yair 25/03/18

Đây dường như là mẫu để sử dụng, ngoại trừ nó không hỗ trợ các phương thức không đồng bộ. Tôi đã tham khảo câu trả lời này và thêm một giải pháp không đồng bộ với Tuple Literals -> tại đây
ttugates

6

Tuy nhiên, một cách tiếp cận khác liên quan đến việc chuyển vào một đối tượng gọi lại hoặc đại biểu sẽ hoạt động dựa trên giá trị. Nếu không tìm thấy giá trị, cuộc gọi lại sẽ không được gọi.

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
}

Điều này hoạt động tốt khi bạn muốn tránh null kiểm tra toàn bộ mã của mình và khi không tìm thấy giá trị không phải là lỗi. Bạn cũng có thể cung cấp một cuộc gọi lại khi không tìm thấy đối tượng nếu bạn cần bất kỳ xử lý đặc biệt nào.

public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
    else
        notFound(); // or notFound.Call();
}

Cách tiếp cận tương tự bằng cách sử dụng một đối tượng có thể trông giống như:

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback.Found(userEntity);
    else
        callback.NotFound();
}

Từ góc độ thiết kế, tôi thực sự thích cách tiếp cận này, nhưng có nhược điểm là làm cho trang web cuộc gọi trở nên cồng kềnh hơn bằng các ngôn ngữ không sẵn sàng hỗ trợ các chức năng hạng nhất.


Hấp dẫn. Khi bạn bắt đầu nói về các đại biểu, tôi ngay lập tức bắt đầu tự hỏi liệu các biểu thức Lambda có thể được sử dụng ở đây không.
7wp

Vâng Theo tôi hiểu, cú pháp lambda của C # 3.0 trở lên về cơ bản là cú pháp đường cho các đại biểu ẩn danh. Tương tự như vậy trong Java, không có cú pháp ủy nhiệm ẩn danh hay lambda đẹp, bạn có thể chỉ cần tạo một lớp ẩn danh. Nó hơi xấu, nhưng có thể thực sự tiện dụng. Tôi cho rằng những ngày này, ví dụ C # của tôi có thể đã sử dụng Func <UserEntity> hoặc một cái gì đó giống như nó thay vì một đại biểu được đặt tên, nhưng dự án C # cuối cùng tôi đã sử dụng vẫn đang sử dụng phiên bản 2.
Marc

+1 Tôi thích cách tiếp cận này. Tuy nhiên, một vấn đề là nó không thông thường và làm tăng nhẹ rào cản gia nhập cơ sở mã.
timoxley

4

Chúng tôi sử dụng CSLA.NET và có quan điểm rằng việc tìm nạp dữ liệu không thành công sẽ trả về một đối tượng "trống". Điều này thực sự khá khó chịu, vì nó yêu cầu quy ước kiểm tra xem có obj.IsNewđúng hơn không obj == null.

Như một poster trước đã đề cập, các giá trị trả về null sẽ khiến mã bị lỗi ngay lập tức, làm giảm khả năng các vấn đề lén lút gây ra bởi các đối tượng trống.

Cá nhân, tôi nghĩ nulllà thanh lịch hơn.

Đây là một trường hợp rất phổ biến và tôi ngạc nhiên khi mọi người ở đây có vẻ ngạc nhiên về nó: trên bất kỳ ứng dụng web nào, dữ liệu thường được tìm nạp bằng tham số chuỗi truy vấn, rõ ràng có thể được xử lý, do đó yêu cầu nhà phát triển xử lý các trường hợp "không tìm thấy ".

Bạn có thể xử lý việc này bằng cách:

if (User.Exists (id)) {
  this .User = User.Fetch (id);
} khác {
  Phản hồi.Redirect ("~ / notfound.aspx");
}

... nhưng đó là một cuộc gọi thêm vào cơ sở dữ liệu mỗi lần, đây có thể là một vấn đề trên các trang lưu lượng truy cập cao. Trong khi:

this .User = User.Fetch (id);

if (this.User == null) {
  Phản hồi.Redirect ("~ / notfound.aspx");
}

... Chỉ cần một cuộc gọi.


4

Tôi thích null, vì nó tương thích với toán tử hợp nhất null ( ??).


4

Tôi muốn trả về null thay vì một đối tượng trống.

Nhưng trường hợp cụ thể mà bạn đã đề cập ở đây, bạn đang tìm kiếm người dùng theo id người dùng, đó là loại khóa cho người dùng đó, trong trường hợp đó tôi có thể muốn ném ngoại lệ nếu không tìm thấy phiên bản người dùng nào .

Đây là quy tắc tôi thường tuân theo:

  • Nếu không tìm thấy kết quả nào trong tìm kiếm bằng thao tác khóa chính, hãy ném ObjectNotFoundException.
  • Nếu không có kết quả nào được tìm thấy trên bất kỳ tiêu chí nào khác, hãy trả về null.
  • Nếu không tìm thấy kết quả nào được tìm thấy bởi một tiêu chí không chính có thể trả về nhiều đối tượng sẽ trả về một bộ sưu tập trống.

Tại sao bạn lại ném một ngoại lệ trong bất kỳ trường hợp nào? Đôi khi người dùng không tồn tại trong cơ sở dữ liệu và chúng tôi hy vọng điều đó có thể không xảy ra. Đó không phải là hành vi đặc biệt.
siride

3

Nó sẽ thay đổi tùy theo ngữ cảnh, nhưng tôi thường trả về null nếu tôi đang tìm một đối tượng cụ thể (như trong ví dụ của bạn) và trả về một bộ sưu tập trống nếu tôi đang tìm một bộ đối tượng nhưng không có đối tượng nào.

Nếu bạn đã mắc lỗi trong mã của mình và trả về null dẫn đến ngoại lệ con trỏ null, thì bạn càng sớm nắm bắt được điều đó càng tốt. Nếu bạn trả về một đối tượng trống, việc sử dụng ban đầu của nó có thể hoạt động, nhưng bạn có thể gặp lỗi sau đó.


+1 Tôi đã đặt câu hỏi giống như logic mà bạn đang nói ở đây, đó là lý do tại sao tôi đăng câu hỏi để xem ý kiến ​​của người khác về vấn đề này sẽ là gì
7wp

3

Tốt nhất trong trường hợp này trả về "null" trong trường hợp không có người dùng như vậy. Cũng làm cho phương thức của bạn tĩnh.

Biên tập:

Thông thường các phương thức như thế này là thành viên của một số lớp "Người dùng" và không có quyền truy cập vào các thành viên thể hiện của nó. Trong trường hợp này, phương thức phải là tĩnh, nếu không, bạn phải tạo một thể hiện của "Người dùng" và sau đó gọi phương thức GetUserById sẽ trả về một thể hiện "Người dùng" khác. Đồng ý điều này là khó hiểu. Nhưng nếu phương thức GetUserById là thành viên của một số lớp "DatabaseFactory" - không có vấn đề gì khi để nó làm thành viên thể hiện.


Tôi có thể hỏi tại sao tôi muốn làm cho phương thức của mình tĩnh không? Nếu tôi muốn sử dụng Dependency Injection thì sao?
7wp

Ok bây giờ tôi nhận được logic của bạn. Nhưng tôi gắn bó với mẫu Kho lưu trữ và tôi thích sử dụng phép nội xạ phụ thuộc cho kho lưu trữ của mình do đó tôi không thể sử dụng các phương thức tĩnh. Nhưng +1 để đề xuất trả về null :)
7wp

3

Cá nhân tôi trả về một thể hiện mặc định của đối tượng. Lý do là tôi hy vọng phương thức trả về 0 cho nhiều hoặc 0 cho một (tùy thuộc vào mục đích của phương thức). Lý do duy nhất mà nó sẽ là một trạng thái lỗi dưới bất kỳ hình thức nào, sử dụng phương pháp này, là nếu phương thức trả về không có đối tượng và luôn được mong đợi (về mặt từ một đến nhiều hoặc trả về số ít).

Đối với giả định rằng đây là một câu hỏi về lĩnh vực kinh doanh - tôi chỉ không thấy nó từ phía đó của phương trình. Chuẩn hóa các kiểu trả về là một câu hỏi kiến ​​trúc ứng dụng hợp lệ. Ít nhất, nó là đối tượng để tiêu chuẩn hóa trong thực hành mã hóa. Tôi nghi ngờ rằng có một người dùng doanh nghiệp sẽ nói "trong kịch bản X, chỉ cần cung cấp cho họ một giá trị null".


+1 Tôi thích quan điểm thay thế của vấn đề. Vì vậy, về cơ bản, bạn đang nói cách tiếp cận nào tôi chọn sẽ tốt miễn là phương pháp này phù hợp trong suốt ứng dụng?
7wp

1
Đó là niềm tin của tôi. Tôi nghĩ tính nhất quán là vô cùng quan trọng. Nếu bạn thực hiện nhiều cách ở nhiều nơi, nó sẽ có nguy cơ cao hơn đối với các lỗi mới. Cá nhân chúng tôi đã sử dụng cách tiếp cận đối tượng mặc định, bởi vì nó hoạt động tốt với mẫu Tinh chất mà chúng tôi sử dụng trong mô hình miền của mình. Chúng tôi có một phương thức mở rộng chung duy nhất mà chúng tôi có thể kiểm tra đối với tất cả các đối tượng miền để cho chúng tôi biết liệu nó có được điền hay không, để chúng tôi biết rằng bất kỳ DO nào cũng có thể được kiểm tra bằng một lệnh gọi của objectname.IsDefault () - tránh mọi kiểm tra đẳng thức trực tiếp .
Joseph Ferris

3

Trong Đối tượng kinh doanh của chúng tôi, chúng tôi có 2 phương thức Nhận chính:

Để giữ mọi thứ đơn giản trong ngữ cảnh hoặc bạn đặt câu hỏi, chúng sẽ là:

// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}

// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}

Phương thức đầu tiên được sử dụng khi nhận các thực thể cụ thể, phương thức thứ hai được sử dụng cụ thể khi thêm hoặc chỉnh sửa các thực thể trên các trang web.

Điều này cho phép chúng ta có được cả hai thế giới tốt nhất trong bối cảnh chúng được sử dụng.


3

Tôi là một sinh viên CNTT người Pháp, vì vậy xin lỗi tiếng Anh của tôi. Trong các lớp của chúng ta, chúng ta được bảo rằng một phương thức như vậy sẽ không bao giờ trả về null, cũng không phải là một đối tượng trống. Người sử dụng phương pháp này được cho là kiểm tra trước xem đối tượng mà anh ta đang tìm kiếm có tồn tại trước khi thử lấy nó không.

Sử dụng Java, chúng tôi được yêu cầu thêm một assert exists(object) : "You shouldn't try to access an object that doesn't exist";từ đầu của bất kỳ phương thức nào có thể trả về null, để diễn tả "điều kiện tiên quyết" (tôi không biết từ này trong tiếng Anh là gì).

IMO này thực sự không dễ sử dụng nhưng đó là những gì tôi đang sử dụng, chờ đợi điều gì đó tốt hơn.


1
Cảm ơn câu trả lời của bạn. Nhưng tôi không thích ý tưởng kiểm tra trước nếu nó tồn tại. Lý do là tạo ra một truy vấn bổ sung cho cơ sở dữ liệu. Trong một ứng dụng đang được hàng triệu người truy cập trong một ngày, điều này có thể dẫn đến mất hiệu suất đáng kể.
7wp

1
Một lợi ích là việc kiểm tra sự tồn tại là trừu tượng một cách thích hợp: if (userExists) dễ đọc hơn một chút, gần với miền vấn đề hơn & ít 'tính toán' hơn: if (user == null)
timoxley

Và tôi sẽ lập luận rằng 'if (x == null)' là một mẫu cũ hàng thập kỷ mà nếu bạn chưa từng thấy nó trước đây, bạn đã không viết mã trong một thời gian dài (và bạn nên làm quen với nó như trong hàng triệu dòng mã). "Tính toán"? Chúng ta đang nói về việc truy cập cơ sở dữ liệu ...
Lloyd Sargent

3

Nếu trường hợp người dùng không được tìm thấy đi lên thường xuyên, và bạn muốn để đối phó với điều đó trong nhiều cách khác nhau tùy thuộc vào hoàn cảnh (đôi khi ném một ngoại lệ, đôi khi thay thế một người sử dụng có sản phẩm nào), bạn cũng có thể sử dụng một cái gì đó gần gũi với 's F # Optionhoặc Haskell Maybeloại , trong đó tách biệt rõ ràng trường hợp 'không có giá trị' từ 'tìm thấy thứ gì đó!'. Mã truy cập cơ sở dữ liệu có thể trông như thế này:

public Option<UserEntity> GetUserById(Guid userId)
{
 //Imagine some code here to access database.....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return Option<UserEntity>.Nothing; 
 else
    return Option.Just(existingUserEntity);
}

Và được sử dụng như thế này:

Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
    // deal with it
} else {
    UserEntity value = result.GetValue();
}

Thật không may, mọi người dường như cuộn một loại như thế này của riêng họ.


2

Tôi thường trả về null. Nó cung cấp một cơ chế nhanh chóng và dễ dàng để phát hiện nếu có thứ gì đó vặn vẹo mà không ném ngoại lệ và sử dụng hàng tấn thử / bắt khắp nơi.


2

Đối với các loại bộ sưu tập, tôi sẽ trả về Bộ sưu tập rỗng, đối với tất cả các loại khác tôi thích sử dụng các mẫu NullObject để trả về một đối tượng thực hiện cùng giao diện với loại trả về. để biết chi tiết về mẫu kiểm tra văn bản liên kết

Sử dụng mẫu NullObject, đây sẽ là: -

public UserEntity GetUserById(Guid userId)

{// Hãy tưởng tượng một số mã ở đây để truy cập cơ sở dữ liệu .....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return new NullUserEntity(); //Should I be doing this here instead? return new UserEntity();  
 else
    return existingUserEntity;

}

class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...} 

2

Nói những gì người khác đã nói một cách sâu sắc hơn ...

Trường hợp ngoại lệ dành cho trường hợp ngoại lệ

Nếu phương thức này là lớp truy cập dữ liệu thuần túy, tôi sẽ nói rằng với một số tham số được bao gồm trong một câu lệnh chọn, tôi sẽ không thể tìm thấy bất kỳ hàng nào để xây dựng một đối tượng và do đó trả về null sẽ được chấp nhận vì điều này là logic truy cập dữ liệu.

Mặt khác, nếu tôi dự đoán tham số của mình sẽ phản ánh khóa chính và tôi chỉ nên lấy lại một hàng, nếu tôi lấy lại được nhiều hơn một thì tôi sẽ ném ngoại lệ. 0 là ok để trả về null, 2 là không.

Bây giờ, nếu tôi có một số mã đăng nhập đã kiểm tra đối với nhà cung cấp LDAP sau đó kiểm tra DB để biết thêm chi tiết và tôi dự kiến ​​chúng sẽ được đồng bộ hóa mọi lúc, tôi có thể ném ngoại lệ sau đó. Như những người khác nói, đó là quy tắc kinh doanh.

Bây giờ tôi sẽ nói đó là một quy tắc chung . Có những lúc bạn có thể muốn phá vỡ điều đó. Tuy nhiên, kinh nghiệm và thí nghiệm của tôi với C # (rất nhiều đó) và Java (một chút về điều đó) đã dạy tôi rằng đó là nhiều hoạt động đắt hơn khôn ngoan để đối phó với trường hợp ngoại lệ hơn để xử lý các vấn đề có thể dự đoán qua logic có điều kiện. Tôi đang nói chuyện với giai điệu 2 hoặc 3 đơn hàng lớn hơn trong một số trường hợp. Vì vậy, nếu có thể mã của bạn có thể kết thúc trong một vòng lặp, thì tôi sẽ khuyên bạn nên trả về null và kiểm tra nó.


2

Tha thứ cho mã giả / php của tôi.

Tôi nghĩ rằng nó thực sự phụ thuộc vào mục đích sử dụng của kết quả.

Nếu bạn có ý định chỉnh sửa / sửa đổi giá trị trả về và lưu nó, sau đó trả về một đối tượng trống. Bằng cách đó, bạn có thể sử dụng cùng một chức năng để điền dữ liệu vào một đối tượng mới hoặc hiện có.

Giả sử tôi có một hàm lấy khóa chính và một mảng dữ liệu, điền vào hàng với dữ liệu, sau đó lưu bản ghi kết quả vào db. Vì tôi đang có ý định điền dữ liệu vào đối tượng bằng dữ liệu của mình, nên có thể là một lợi thế rất lớn để lấy lại một đối tượng trống từ getter. Bằng cách đó, tôi có thể thực hiện các hoạt động giống hệt nhau trong cả hai trường hợp. Bạn sử dụng kết quả của hàm getter không có vấn đề gì.

Thí dụ:

function saveTheRow($prim_key, $data) {
    $row = getRowByPrimKey($prim_key);

    // Populate the data here

    $row->save();
}

Ở đây chúng ta có thể thấy rằng cùng một chuỗi các thao tác thao túng tất cả các bản ghi loại này.

Tuy nhiên, nếu mục đích cuối cùng của giá trị trả về là đọc và làm một cái gì đó với dữ liệu, thì tôi sẽ trả về null. Bằng cách này, tôi có thể nhanh chóng xác định xem không có dữ liệu nào được trả về và hiển thị thông báo phù hợp cho người dùng.

Thông thường, tôi sẽ bắt ngoại lệ trong chức năng của mình để lấy dữ liệu (vì vậy tôi có thể ghi nhật ký thông báo lỗi, v.v ...) sau đó trả về null ngay từ lệnh bắt. Nói chung, vấn đề không phải là vấn đề đối với người dùng cuối, vấn đề là gì, vì vậy tôi thấy tốt nhất là gói gọn việc ghi nhật ký / xử lý lỗi của mình trực tiếp trong chức năng lấy dữ liệu. Nếu bạn đang duy trì một cơ sở mã được chia sẻ trong bất kỳ công ty lớn nào thì điều này đặc biệt có lợi vì bạn có thể buộc ghi nhật ký / xử lý lỗi thích hợp ngay cả với lập trình viên lười nhất.

Thí dụ:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Đó là quy tắc chung của tôi. Nó đã hoạt động tốt cho đến nay.


2

Câu hỏi thú vị và tôi nghĩ không có câu trả lời "đúng", vì nó luôn phụ thuộc vào trách nhiệm của mã của bạn. Phương pháp của bạn có biết nếu không tìm thấy dữ liệu là một vấn đề hay không? Trong hầu hết các trường hợp, câu trả lời là "không" và đó là lý do tại sao trả về null và để người gọi xử lý tình huống của anh ta là hoàn hảo.

Có lẽ một cách tiếp cận tốt để phân biệt các phương thức ném với các phương thức trả về null là tìm một quy ước trong nhóm của bạn: Các phương thức nói rằng chúng "có được" thứ gì đó nên ném ngoại lệ nếu không có gì để lấy. Các phương thức có thể trả về null có thể được đặt tên khác, có lẽ là "Tìm ...".


+1 Tôi thích ý tưởng sử dụng quy ước đặt tên thống nhất để báo hiệu cho người lập trình cách sử dụng chức năng đó.
7wp

1
Đột nhiên tôi nhận ra rằng đây là những gì LINQ làm: xem xét First (...) so với FirstOrDefault (...)
Marc Wittke

2

Nếu đối tượng được trả về là thứ có thể lặp đi lặp lại, tôi sẽ trả lại một đối tượng trống, để tôi không phải kiểm tra null trước.

Thí dụ:

bool IsAdministrator(User user)
{
    var groupsOfUser = GetGroupsOfUser(user);

    // This foreach would cause a run time exception if groupsOfUser is null.
    foreach (var groupOfUser in groupsOfUser) 
    {
        if (groupOfUser.Name == "Administrators")
        {
            return true;
        }
    }

    return false;
}

2

Tôi muốn không trả về null từ bất kỳ phương thức nào, nhưng để sử dụng loại chức năng Tùy chọn thay thế. Các phương thức có thể trả về không có kết quả trả về một Tùy chọn trống, thay vì null.

Ngoài ra, các phương thức như vậy có thể trả về không có kết quả sẽ chỉ ra rằng thông qua tên của chúng. Tôi thường đặt Try hoặc TryGet hoặc TryFind ở đầu tên của phương thức để chỉ ra rằng nó có thể trả về một kết quả trống (ví dụ: TryFindCustomer, TryLoadFile, v.v.).

Điều đó cho phép người gọi áp dụng các kỹ thuật khác nhau, như đường ống thu thập (xem Đường ống thu thập của Martin Fowler ) trên kết quả.

Dưới đây là một ví dụ khác khi trả về Tùy chọn thay vì null được sử dụng để giảm độ phức tạp của mã: Cách giảm độ phức tạp theo chu kỳ: Loại chức năng tùy chọn


1
Tôi đã viết một câu trả lời, tôi có thể thấy nó tương tự như của bạn khi tôi cuộn lên và tôi đồng ý, bạn có thể thực hiện một loại tùy chọn với một bộ sưu tập chung có 0 hoặc 1 phần tử. Cảm ơn các liên kết bổ sung.
Gabriel P.

1

Thêm thịt để xay: giả sử DAL của tôi trả lại NULL cho GetPersonByID theo lời khuyên của một số người. BLL (khá mỏng) của tôi nên làm gì nếu nhận được NULL? Vượt qua NULL đó và để người tiêu dùng cuối lo lắng về điều đó (trong trường hợp này là trang ASP.Net)? Làm thế nào về việc BLL ném một ngoại lệ?

BLL có thể đang được sử dụng bởi ASP.Net và Win App hoặc thư viện lớp khác - Tôi nghĩ thật không công bằng khi hy vọng người tiêu dùng cuối thực sự "biết" rằng phương thức GetPersonByID trả về null (trừ khi sử dụng loại null, tôi đoán vậy ).

Tôi nhận (với giá trị của nó) là DAL của tôi trả về NULL nếu không tìm thấy gì. ĐỐI VỚI MỘT SỐ ĐỐI TƯỢNG, điều đó ổn - có thể là 0: nhiều danh sách các thứ, vì vậy không có bất kỳ thứ gì là tốt (ví dụ: danh sách các cuốn sách yêu thích). Trong trường hợp này, BLL của tôi trả về một danh sách trống. Đối với hầu hết mọi thứ đơn lẻ (ví dụ: người dùng, tài khoản, hóa đơn) nếu tôi không có, thì đó chắc chắn là một vấn đề và là một ngoại lệ tốn kém. Tuy nhiên, xem việc truy xuất người dùng bằng một mã định danh duy nhất mà ứng dụng đã cung cấp trước đó sẽ luôn trả về người dùng, ngoại lệ là một ngoại lệ "phù hợp", như trong trường hợp đặc biệt. Người tiêu dùng cuối cùng của BLL (ASP.Net, f'rinstance) chỉ mong muốn mọi thứ trở nên hunky-dory, do đó, một Trình xử lý ngoại lệ chưa được xử lý sẽ được sử dụng thay vì gói mọi cuộc gọi đến GetPersonByID trong một khối thử.

Nếu có một vấn đề rõ ràng trong cách tiếp cận của tôi, xin vui lòng cho tôi biết vì tôi luôn muốn học hỏi. Như các áp phích khác đã nói, các trường hợp ngoại lệ là những thứ tốn kém, và phương pháp "kiểm tra trước" là tốt, nhưng ngoại lệ chỉ là điều đó - đặc biệt.

Tôi rất thích bài đăng này, rất nhiều gợi ý hay cho các tình huống "nó phụ thuộc" :-)


Và tất nhiên, hôm nay tôi đã bắt gặp một kịch bản mà tôi sẽ trả lại NULL từ BLL của mình ;-) Điều đó nói rằng, tôi vẫn có thể ném một ngoại lệ và sử dụng thử / bắt trong lớp tiêu dùng của mình NHƯNG tôi vẫn gặp vấn đề : làm thế nào để lớp tiêu thụ của tôi biết sử dụng thử / bắt, tương tự như thế nào để họ biết kiểm tra NULL?
Mike Kingscott

Bạn có thể ghi lại rằng một phương thức đưa ra một ngoại lệ thông qua tài liệu @throws và bạn sẽ ghi lại thực tế rằng nó có thể trả về null trong tài liệu @return.
timoxley

1

Tôi nghĩ rằng các hàm không nên trả về null, vì sức khỏe của cơ sở mã của bạn. Tôi có thể nghĩ ra một vài lý do:

Sẽ có một số lượng lớn các mệnh đề bảo vệ tham chiếu null if (f() != null).

Đó nulllà một câu trả lời được chấp nhận hay một vấn đề? Là null một trạng thái hợp lệ cho một đối tượng cụ thể? (hãy tưởng tượng rằng bạn là một khách hàng cho mã). Tôi có nghĩa là tất cả các loại tham chiếu có thể là null, nhưng họ nên?

nulltreo xung quanh sẽ hầu như luôn luôn đưa ra một vài trường hợp ngoại lệ NullRef bất ngờ bất cứ lúc nào như mã-base của bạn phát triển.

Có một số giải pháp, tester-doer patternhoặc thực hiện option typetừ lập trình chức năng.


0

Tôi bối rối trước số lượng câu trả lời (trên tất cả các trang web) nói rằng bạn cần hai phương thức: phương thức "IsItThere ()" và phương thức "GetItForMe ()" và do đó điều này dẫn đến tình trạng chủng tộc. Điều gì sai với hàm trả về null, gán nó cho một biến và kiểm tra biến cho Null all trong một bài kiểm tra? Mã C cũ của tôi đã bị tiêu

if (NULL! = (biến = hàm (đối số ...))) {

Vì vậy, bạn nhận được giá trị (hoặc null) trong một biến và kết quả tất cả cùng một lúc. Có thành ngữ này đã bị lãng quên? Tại sao?


0

Tôi đồng ý với hầu hết các bài viết ở đây, có xu hướng null .

Lý do của tôi là việc tạo ra một đối tượng trống với các thuộc tính không thể rỗng có thể gây ra lỗi. Ví dụ: một thực thể có thuộc int IDtính sẽ có giá trị ban đầu làID = 0 hoàn toàn hợp lệ. Nếu đối tượng đó, trong một số trường hợp, được lưu vào cơ sở dữ liệu, nó sẽ là một điều xấu.

Đối với bất cứ điều gì với một trình vòng lặp, tôi sẽ luôn sử dụng bộ sưu tập trống. Cái gì đó như

foreach (var eachValue in collection ?? new List<Type>(0))

là mùi mã theo ý kiến ​​của tôi. Bộ sưu tập thuộc tính không nên là null, bao giờ hết.

Một trường hợp cạnh là String. Nhiều người nói, String.IsNullOrEmptykhông thực sự cần thiết, nhưng bạn không thể luôn phân biệt giữa một chuỗi rỗng và null. Hơn nữa, một số hệ thống cơ sở dữ liệu (Oracle) sẽ không phân biệt giữa chúng ( ''được lưu trữ dưới dạng DBNULL), vì vậy bạn buộc phải xử lý chúng như nhau. Lý do cho điều đó là, hầu hết các giá trị chuỗi đều đến từ đầu vào của người dùng hoặc từ các hệ thống bên ngoài, trong khi cả hộp văn bản và hầu hết các định dạng trao đổi đều có các cách biểu diễn khác nhau cho ''null. Vì vậy, ngay cả khi người dùng muốn xóa một giá trị, anh ta không thể làm gì hơn là xóa điều khiển đầu vào. Ngoài ra, sự khác biệt của nvarcharcác trường cơ sở dữ liệu nullable và không nullable là đáng nghi ngờ hơn, nếu DBMS của bạn không phải là orory - một trường bắt buộc cho phép''là lạ, giao diện người dùng của bạn sẽ không bao giờ cho phép điều này, vì vậy các ràng buộc của bạn không ánh xạ. Vì vậy, câu trả lời ở đây, theo tôi là, xử lý chúng như nhau, luôn luôn.

Liên quan đến câu hỏi của bạn liên quan đến ngoại lệ và hiệu suất: Nếu bạn đưa ra một ngoại lệ mà bạn không thể xử lý hoàn toàn trong logic chương trình của mình, bạn phải hủy bỏ, tại một số điểm, bất kể chương trình của bạn đang làm gì và yêu cầu người dùng làm lại bất cứ điều gì anh ta vừa làm. Trong trường hợp đó, hình phạt hiệu suất của a catchthực sự là ít lo lắng nhất của bạn - phải hỏi người dùng là con voi trong phòng (có nghĩa là hiển thị lại toàn bộ UI hoặc gửi một số HTML qua internet). Vì vậy, nếu bạn không tuân theo mô hình chống " Dòng chảy chương trình với ngoại lệ ", đừng bận tâm, hãy ném nó nếu nó hợp lý. Ngay cả trong các trường hợp đường biên, chẳng hạn như "Ngoại lệ xác thực", hiệu suất thực sự không phải là vấn đề, vì bạn phải hỏi lại người dùng, trong mọi trường hợp.


0

Một Asynchronous TryGet Pattern:

Đối với phương pháp đồng bộ, tôi tin rằng @Johann Gerell của câu trả lờicác mẫu để sử dụng trong mọi trường hợp.

Tuy nhiên, mẫu TryGet với outtham số không hoạt động với các phương thức Async.

Với Tuple Literals của C # 7, giờ đây bạn có thể làm điều này:

async Task<(bool success, SomeObject o)> TryGetSomeObjectByIdAsync(Int32 id)
{
    if (InternalIdExists(id))
    {
        o = await InternalGetSomeObjectAsync(id);

        return (true, o);
    }
    else
    {
        return (false, default(SomeObject));
    }
}
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.