Làm cách nào tôi có thể trả về NULL từ một phương thức chung trong C #?


546

Tôi có một phương pháp chung với mã (giả) này (vâng tôi biết IList có các vị từ, nhưng mã của tôi không sử dụng IList mà là một số bộ sưu tập khác, dù sao thì điều này không liên quan đến câu hỏi ...)

static T FindThing<T>(IList collection, int id) where T : IThing, new()
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;  // ERROR: Cannot convert null to type parameter 'T' because it could be a value type. Consider using 'default(T)' instead.
}

Điều này mang lại cho tôi một lỗi xây dựng

"Không thể chuyển đổi null thành loại tham số 'T' vì đó có thể là loại giá trị. Thay vào đó, hãy xem xét sử dụng 'mặc định (T)'."

Tôi có thể tránh lỗi này không?


Các loại tham chiếu nullable (trong C # 8) sẽ là giải pháp tốt hơn cho vấn đề này bây giờ? docs.microsoft.com/en-us/dotnet/csharp/nullable-references Trả lại nullbất kể Tlà có Objecthay intkhông char.
Alexander - Phục hồi lại

Câu trả lời:


968

Hai lựa chọn:

  • Trả về default(T)nghĩa là bạn sẽ trả về nullnếu T là loại tham chiếu (hoặc loại giá trị không thể), 0for int, '\0'for char, v.v. ( Bảng giá trị mặc định (Tham chiếu C #) )
  • Hạn chế T là loại tham chiếu có where T : classràng buộc và sau đó trả về nullnhư bình thường

3
Điều gì xảy ra nếu kiểu trả về của tôi là một enum không phải là một lớp? Tôi không thể chỉ định T: enum :(
Justin

1
Trong .NET, enum là một trình bao bọc rất mỏng (và khá rò rỉ) xung quanh một kiểu số nguyên. Quy ước là sử dụng số không cho giá trị enum "mặc định" của bạn.
Mike Chamberlain

27
Tôi nghĩ vấn đề với điều này là nếu bạn đang sử dụng phương thức chung này để nói, hãy chuyển đổi một đối tượng Cơ sở dữ liệu từ DbNull sang Int và nó trả về mặc định (T) trong đó T là một số nguyên, nó sẽ trả về 0. Nếu số này là thực sự có ý nghĩa, sau đó bạn sẽ chuyển xung quanh dữ liệu xấu trong trường hợp trường đó là null. Hoặc một ví dụ tốt hơn sẽ là DateTime. Nếu trường giống như "DateCloses" và nó được trả về là null vì tài khoản vẫn mở, thì nó thực sự sẽ mặc định (DateTime) thành 1/1/0000, ngụ ý rằng tài khoản đã bị đóng trước khi máy tính được phát minh.
Thời báo

21
@ Thẩm mỹ: Vì vậy, bạn sẽ chuyển đổi sang Nullable<int>hoặc Nullable<DateTime>thay vào đó. Nếu bạn sử dụng loại không nullable và cần đại diện cho một giá trị null, bạn chỉ đang yêu cầu sự cố.
Jon Skeet

1
Tôi đồng ý, tôi chỉ muốn đưa nó lên. Tôi nghĩ những gì tôi đã làm giống như MyMethod <T> (); giả sử nó là loại không nullable và MyMethod <T?> (); giả sử nó là một loại nullable. Vì vậy, trong các kịch bản của tôi, tôi có thể sử dụng biến tạm thời để bắt null và đi từ đó.
Thời báo

84
return default(T);

Liên kết này: msdn.microsoft.com/en-us/l Library / xwth0h0d (VS.80) .aspx nên giải thích lý do tại sao.
Harper Shelby

1
Chết tiệt, tôi đã tiết kiệm được rất nhiều thời gian tôi đã biết về từ khóa này - cảm ơn Ricardo!
Ana Betts

1
Tôi ngạc nhiên vì điều này đã không nhận được nhiều phiếu bầu hơn vì từ khóa 'mặc định' là một giải pháp toàn diện hơn, cho phép sử dụng các loại không tham chiếu kết hợp với các loại và cấu trúc số. Mặc dù câu trả lời được chấp nhận giải quyết vấn đề (và thực sự hữu ích), nhưng câu trả lời tốt hơn là làm thế nào để hạn chế loại trả về thành loại không thể / tham chiếu.
Steve Jackson

33

Bạn chỉ có thể điều chỉnh các ràng buộc của mình:

where T : class

Sau đó trả về null được cho phép.


Cảm ơn. Tôi không thể chọn 2 câu trả lời là giải pháp được chấp nhận, vì vậy tôi chọn John Skeet vì câu trả lời của anh ấy có hai giải pháp.
edosoft

@Migol nó phụ thuộc vào yêu cầu của bạn. Có lẽ dự án của họ không yêu cầu nó được IDisposable. Vâng, hầu hết thời gian nó không phải là. System.Stringkhông thực hiện IDisposable, ví dụ. Người trả lời nên đã làm rõ điều đó, nhưng điều đó không làm cho câu trả lời sai. :)
ahwm

@Migol Tôi không biết tại sao tôi lại có IDis Dùng ở đó. Đã xóa.
TheSoftwareJedi

13

Thêm ràng buộc lớp làm ràng buộc đầu tiên cho loại chung của bạn.

static T FindThing<T>(IList collection, int id) where T : class, IThing, new()

Cảm ơn. Tôi không thể chọn 2 câu trả lời là giải pháp được chấp nhận, vì vậy tôi chọn John Skeet vì câu trả lời của anh ấy có hai giải pháp.
edosoft

7
  1. Nếu bạn có đối tượng thì cần đánh máy

    return (T)(object)(employee);
  2. nếu bạn cần trả về null.

    return default(T);

Xin chào user725388, vui lòng xác minh tùy chọn đầu tiên
Jogi Joseph George

7

Dưới đây là hai tùy chọn bạn có thể sử dụng

return default(T);

hoặc là

where T : class, IThing
 return null;

6

Tùy chọn khác của bạn sẽ là thêm phần này vào cuối tờ khai của bạn:

    where T : class
    where T: IList

Bằng cách đó, nó sẽ cho phép bạn trả về null.


Nếu cả hai ràng buộc là cùng một loại, bạn đề cập đến loại một lần và sử dụng dấu phẩy, như where T : class, IList. Nếu bạn có các ràng buộc đối với các loại khác nhau, bạn lặp lại mã thông báo where, như trong where TFoo : class where TBar : IList.
Jeppe Stig Nielsen

3

giải pháp của TheSoftwareJedi hoạt động,

bạn cũng có thể lưu trữ nó bằng cách sử dụng một vài loại giá trị và nullable:

static T? FindThing<T>(IList collection, int id) where T : struct, IThing
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return thing;
    }
    return null;
}

1

Đưa ra khuyến nghị về lỗi ... và người dùng default(T)hoặc new T.

Bạn sẽ phải thêm vào một so sánh trong mã của mình để đảm bảo rằng đó là một kết quả khớp hợp lệ nếu bạn đi theo tuyến đường đó.

Mặt khác, có khả năng xem xét một tham số đầu ra cho "tìm thấy kết quả khớp".


1

Đây là một ví dụ hoạt động cho các giá trị trả về Nullable Enum:

public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct
{
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value);
}

Kể từ C # 7.3 (tháng 5 năm 2018), bạn có thể cải thiện các ràng buộc thành where TEnum : struct, Enum. Điều này đảm bảo rằng người gọi không vô tình cung cấp một loại giá trị không phải là enum (chẳng hạn như một inthoặc a DateTime).
Jeppe Stig Nielsen

0

Một cách khác để 2 câu trả lời được trình bày ở trên. Nếu bạn thay đổi loại trả về của mình thành object, bạn có thể trả về null, đồng thời bỏ trả về giá trị không null.

static object FindThing<T>(IList collection, int id)
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return (T) thing;
    }
    return null;  // allowed now
}

Nhược điểm: điều này sẽ yêu cầu người gọi phương thức truyền đối tượng được trả về (trong trường hợp không null), hàm ý quyền anh -> hiệu suất thấp hơn. Tôi có đúng không
Csharpest

0

Để hoàn thiện, thật tốt khi biết bạn cũng có thể làm điều này:

return default;

Nó trả về giống như return default(T);


0

Đối với tôi nó là công việc như nó là. Chính xác thì vấn đề ở đâu?

public static T FindThing<T>(this IList collection, int id) where T : IThing, new()
{
    foreach (T thing in collection)
    {
        if (thing.Id == id)
            return thing;
        }
    }

    return null; //work
    return (T)null; //work
    return null as T; //work
    return default(T); //work


}
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.