Wie kann ich NULL von einer generischen Methode in C # zurückgeben?

546

Ich habe eine generische Methode mit diesem (Dummy-) Code (ja, ich weiß, dass IList Prädikate hat, aber mein Code verwendet nicht IList, sondern eine andere Sammlung, dies ist jedoch für die Frage irrelevant ...)

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

Dies gibt mir einen Build-Fehler

"Null kann nicht in den Typparameter 'T' konvertiert werden, da es sich um einen Werttyp handeln kann. Verwenden Sie stattdessen 'default (T)'."

Kann ich diesen Fehler vermeiden?

edosoft
quelle
Wären nullfähige Referenztypen (in C # 8) jetzt die bessere Lösung dafür? docs.microsoft.com/en-us/dotnet/csharp/nullable-references Rückkehr nullunabhängig davon , ob Tist Objectoder intoder char.
Alexander - Monica

Antworten:

968

Zwei Optionen:

  • Rückgabe, default(T)was bedeutet, dass Sie zurückkehren, nullwenn T ein Referenztyp (oder ein nullwertfähiger Werttyp) 0für int, '\0'für charusw. ist ( Standardwertetabelle (C # -Referenz) )
  • Beschränken Sie T auf einen Referenztyp mit der where T : classEinschränkung und kehren Sie dann nullwie gewohnt zurück
Jon Skeet
quelle
3
Was ist, wenn mein Rückgabetyp eine Aufzählung und keine Klasse ist? Ich kann T: enum :(
Justin
1
In .NET ist eine Aufzählung ein sehr dünner (und ziemlich undichter) Wrapper um einen ganzzahligen Typ. Die Konvention besteht darin, Null für Ihren "Standard" -Aufzählungswert zu verwenden.
Mike Chamberlain
27
Ich denke, das Problem dabei ist, dass, wenn Sie diese generische Methode verwenden, um zu sagen, ein Datenbankobjekt von DbNull in Int konvertieren und es Standard (T) zurückgibt, wobei T ein int ist, es 0 zurückgibt. Wenn diese Zahl ist tatsächlich sinnvoll, dann würden Sie schlechte Daten in Fällen weitergeben, in denen dieses Feld null war. Oder ein besseres Beispiel wäre eine DateTime. Wenn das Feld so etwas wie "DateClosed" war und als null zurückgegeben wurde, weil das Konto noch geöffnet ist, wird standardmäßig (DateTime) der 1.1.0000 verwendet, was bedeutet, dass das Konto geschlossen wurde, bevor Computer erfunden wurden.
Sinaesthetic
21
@Sinaesthetic: Also würden Sie zu Nullable<int>oder Nullable<DateTime>stattdessen konvertieren . Wenn Sie einen nicht nullbaren Typ verwenden und einen Nullwert darstellen müssen, fragen Sie nur nach Problemen.
Jon Skeet
1
Ich stimme zu, ich wollte es nur ansprechen. Ich denke, was ich getan habe, ähnelt eher MyMethod <T> (); anzunehmen, dass es sich um einen nicht nullbaren Typ handelt und MyMethod <T?> (); um anzunehmen, dass es sich um einen nullbaren Typ handelt. In meinen Szenarien könnte ich also eine temporäre Variable verwenden, um eine Null abzufangen und von dort fortzufahren.
Sinaesthetic
84
return default(T);
Ricardo Villamil
quelle
Dieser Link: msdn.microsoft.com/en-us/library/xwth0h0d(VS.80).aspx sollte erklären, warum.
Harper Shelby
1
Verdammt, ich hätte viel Zeit gespart, wenn ich von diesem Keyword gewusst hätte - danke Ricardo!
Ana Betts
1
Ich bin überrascht, dass dies nicht zu mehr Stimmen geführt hat, da das Standardschlüsselwort eine umfassendere Lösung ist, die die Verwendung von Nichtreferenztypen in Verbindung mit numerischen Typen und Strukturen ermöglicht. Während die akzeptierte Antwort das Problem löst (und in der Tat hilfreich ist), beantwortet sie besser, wie der Rückgabetyp auf nullbare / Referenztypen beschränkt werden kann.
Steve Jackson
33

Sie können einfach Ihre Einschränkungen anpassen:

where T : class

Dann ist die Rückgabe von null zulässig.

TheSoftwareJedi
quelle
Vielen Dank. Ich kann nicht 2 Antworten als akzeptierte Lösung auswählen, daher wähle ich John Skeets, da seine Antwort zwei Lösungen enthält.
Edosoft
@ Migol es hängt von deinen Anforderungen ab. Vielleicht erfordert ihr Projekt es IDisposable. Ja, meistens muss es nicht sein. System.Stringimplementiert IDisposablezum Beispiel nicht. Der Antwortende hätte das klarstellen sollen, aber das macht die Antwort nicht falsch. :)
ahwm
@ Migol Ich habe keine Ahnung, warum ich dort IDisposable hatte. Entfernt.
TheSoftwareJedi
13

Fügen Sie die Klasseneinschränkung als erste Einschränkung zu Ihrem generischen Typ hinzu.

static T FindThing<T>(IList collection, int id) where T : class, IThing, new()
Mindest
quelle
Vielen Dank. Ich kann nicht 2 Antworten als akzeptierte Lösung auswählen, daher wähle ich John Skeets, da seine Antwort zwei Lösungen enthält.
Edosoft
7
  1. Wenn Sie ein Objekt haben, müssen Sie es typisieren

    return (T)(object)(employee);
  2. wenn Sie null zurückgeben müssen.

    return default(T);
Benutzer725388
quelle
Hallo Benutzer725388, bitte überprüfen Sie die erste Option
Jogi Joseph George
7

Nachfolgend sind die beiden Optionen aufgeführt, die Sie verwenden können

return default(T);

oder

where T : class, IThing
 return null;
Jaydeep Shil
quelle
6

Sie können dies auch am Ende Ihrer Erklärung hinzufügen:

    where T : class
    where T: IList

Auf diese Weise können Sie null zurückgeben.

BKostenlos
quelle
Wenn beide Einschränkungen denselben Typ haben, erwähnen Sie den Typ einmal und verwenden ein Komma wie where T : class, IList. Wenn Sie Einschränkungen für verschiedene Typen haben, wiederholen Sie das Token wherewie in where TFoo : class where TBar : IList.
Jeppe Stig Nielsen
3

Lösung von TheSoftwareJedi funktioniert,

Sie können es auch mit einigen wert- und nullbaren Typen archivieren:

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;
}
devi
quelle
1

Nehmen Sie die Empfehlung des Fehlers ... und entweder Benutzer default(T)oder new T.

Sie müssen einen Vergleich in Ihren Code einfügen, um sicherzustellen, dass es sich um eine gültige Übereinstimmung handelt, wenn Sie diesen Weg gehen.

Andernfalls sollten Sie möglicherweise einen Ausgabeparameter für "Übereinstimmung gefunden" in Betracht ziehen.

Mitchel Sellers
quelle
1

Hier ist ein Arbeitsbeispiel für Nullable Enum-Rückgabewerte:

public static TEnum? ParseOptional<TEnum>(this string value) where TEnum : struct
{
    return value == null ? (TEnum?)null : (TEnum) Enum.Parse(typeof(TEnum), value);
}
Luke
quelle
Seit C # 7.3 (Mai 2018) können Sie die Einschränkung auf verbessern where TEnum : struct, Enum. Dies stellt sicher, dass ein Anrufer nicht versehentlich einen Werttyp angibt, der keine Aufzählung ist (z. B. ein intoder ein DateTime).
Jeppe Stig Nielsen
0

Eine weitere Alternative zu 2 oben dargestellten Antworten. Wenn Sie Ihren Rückgabetyp in ändern object, können Sie zurückgeben nullund gleichzeitig die Nicht-Null-Rückgabe umwandeln.

static object FindThing<T>(IList collection, int id)
{
    foreach T thing in collecion
    {
        if (thing.Id == id)
            return (T) thing;
    }
    return null;  // allowed now
}
Jeson Martajaya
quelle
Nachteil: Dies würde erfordern, dass der Aufrufer der Methode das zurückgegebene Objekt umwandelt (im Fall ungleich Null), was Boxen impliziert -> weniger Leistung. Habe ich recht?
Csharpest
0

Der Vollständigkeit halber ist es gut zu wissen, dass Sie dies auch tun können:

return default;

Es gibt dasselbe zurück wie return default(T);

LCIII
quelle
0

Für mich funktioniert es so wie es ist. Wo genau liegt das 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


}
Mertuarez
quelle