Jak mogę zwrócić NULL z ogólnej metody w C #?


546

Mam ogólną metodę z tym (obojętnym) kodem (tak, wiem, że IList ma predykaty, ale mój kod nie używa IList, ale innej kolekcji, w każdym razie nie ma to znaczenia dla pytania ...)

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

Daje mi to błąd kompilacji

„Nie można przekonwertować wartości null na parametr„ T ”, ponieważ może to być typ wartości. Rozważ użycie zamiast tego parametru„ default (T) ”.”

Czy mogę uniknąć tego błędu?


Czy typy zerowalne odniesienia (w C # 8) byłyby teraz lepszym rozwiązaniem? docs.microsoft.com/en-us/dotnet/csharp/nullable-references Zwracanie nullbez względu na Tto, czy jest Objectlub intczy char.
Alexander - Przywróć Monikę

Odpowiedzi:


968

Dwie opcje:

  • Zwróć, default(T)co oznacza, że ​​zwrócisz, nulljeśli T jest typem odniesienia (lub typem wartości zerowej), 0for int, '\0'for charitp. ( Tabela wartości domyślnych (odniesienie C #) )
  • Ogranicz T, aby był typem odniesienia z where T : classograniczeniem, a następnie wróć nullnormalnie

3
Co jeśli mój typ zwrotu nie jest klasą enum? Nie mogę określić T: enum :(
Justin

1
W .NET wyliczenie jest bardzo cienkim (i raczej nieszczelnym) opakowaniem wokół typu liczb całkowitych. Konwencja polega na użyciu zera dla „domyślnej” wartości wyliczenia.
Mike Chamberlain,

27
Myślę, że problem polega na tym, że jeśli używasz tej ogólnej metody do powiedzenia, przekonwertuj obiekt bazy danych z DbNull na Int i zwróci domyślną (T) gdzie T jest int, zwróci 0. Jeśli ta liczba to naprawdę sensowne, wtedy przekazywałbyś złe dane w przypadkach, gdy pole to było puste. Lub lepszym przykładem może być DateTime. Jeśli pole było coś w rodzaju „Data zamknięta” i zostało zwrócone jako null, ponieważ i konto jest nadal otwarte, w rzeczywistości miałoby wartość domyślną (DateTime) na 1/1/0000, co oznacza, że ​​konto zostało zamknięte przed wynalezieniem komputerów.
Sinaesthetic,

21
@Sinaesthetic: więc możesz przejść na Nullable<int>lub Nullable<DateTime>zamiast tego. Jeśli używasz typu, który nie ma wartości zerowej, i chcesz przedstawić wartość zerową, po prostu prosisz o kłopoty.
Jon Skeet,

1
Zgadzam się, chciałem to tylko omówić. Myślę, że to, co robiłem, bardziej przypomina MyMethod <T> (); założyć, że jest to typ zerowalny i MyMethod <T?> (); założyć, że jest to typ zerowalny. Tak więc w moich scenariuszach mogłem użyć zmiennej temp, aby złapać wartość null i przejść od tego momentu.
Sinaesthetic,

84
return default(T);


1
Cholera, zaoszczędziłbym dużo czasu, gdybym wiedział o tym słowie kluczowym - dzięki Ricardo!
Ana Betts,

1
Dziwię się, że nie zyskało to więcej głosów, ponieważ „domyślne” słowo kluczowe jest bardziej wszechstronnym rozwiązaniem, umożliwiającym stosowanie typów innych niż odniesienia w połączeniu z typami liczbowymi i strukturami. Chociaż zaakceptowana odpowiedź rozwiązuje problem (i rzeczywiście jest pomocna), lepiej odpowiada, w jaki sposób ograniczyć typ zwracany do typów zerowalnych / referencyjnych.
Steve Jackson

33

Możesz po prostu dostosować swoje ograniczenia:

where T : class

Wówczas dozwolone jest zwracanie wartości null.


Dzięki. Nie mogę wybrać 2 odpowiedzi jako przyjętego rozwiązania, więc wybieram Johna Skeeta, ponieważ jego odpowiedź ma dwa rozwiązania.
edosoft,

@Migol zależy od twoich wymagań. Może ich projekt tego wymaga IDisposable. Tak, przez większość czasu tak nie musi być. na przykład System.Stringnie implementuje IDisposable. Odpowiadający powinien to wyjaśnić, ale to nie czyni odpowiedzi błędną. :)
ahwm

@Migol Nie mam pojęcia, dlaczego miałem tam IDisposable. Oddalony.
TheSoftwareJedi

13

Dodaj ograniczenie klasy jako pierwsze ograniczenie do typu ogólnego.

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

Dzięki. Nie mogę wybrać 2 odpowiedzi jako przyjętego rozwiązania, więc wybieram Johna Skeeta, ponieważ jego odpowiedź ma dwa rozwiązania.
edosoft,

7
  1. Jeśli masz obiekt, musisz rzutować czcionką

    return (T)(object)(employee);
  2. jeśli musisz zwrócić null.

    return default(T);

Witaj user725388, sprawdź pierwszą opcję
Jogi Joseph George

7

Poniżej znajdują się dwie opcje, których możesz użyć

return default(T);

lub

where T : class, IThing
 return null;

6

Inną opcją byłoby dodanie tego na końcu deklaracji:

    where T : class
    where T: IList

W ten sposób pozwoli ci zwrócić wartość null.


Jeśli oba ograniczenia dotyczą tego samego typu, należy wspomnieć o tym typie raz i użyć przecinka, np where T : class, IList. Jeśli masz ograniczenia do różnych typów, powtórz token where, jak w where TFoo : class where TBar : IList.
Jeppe Stig Nielsen

3

rozwiązanie prac TheSoftwareJedi,

możesz także zarchiwizować go, używając kilku typów wartości i dopuszczających wartości zerowe:

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

Weź zalecenie błędu ... i albo użytkownika default(T)albo new T.

Musisz dodać porównanie w kodzie, aby upewnić się, że było to prawidłowe dopasowanie, jeśli wybierzesz tę trasę.

W przeciwnym razie potencjalnie rozważ parametr wyjściowy dla „znaleziono dopasowanie”.


1

Oto działający przykład zwracanych wartości Nullable Enum:

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

Od wersji C # 7.3 (maj 2018) można poprawić ograniczenie do where TEnum : struct, Enum. Zapewnia to, że osoba dzwoniąca nie poda przypadkowo typu wartości, który nie jest wyliczeniem (takim jak a intlub a DateTime).
Jeppe Stig Nielsen

0

Kolejna alternatywa dla 2 odpowiedzi przedstawionych powyżej. Jeśli zmienisz typ zwrotu na object, możesz wrócić null, jednocześnie rzucając zwrot inny niż 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
}

Wada: wymagałoby to od osoby wywołującej metodę rzutowania zwróconego obiektu (w przypadku różnym od zera), co oznacza boks -> mniejszą wydajność. Czy mam rację?
Csharpest

0

Dla kompletności dobrze jest wiedzieć, że możesz to zrobić również:

return default;

Zwraca to samo co return default(T);


0

Dla mnie to działa tak, jak jest. Gdzie dokładnie jest problem?

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


}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.