2012-10-30 6 views
8

Angenommen, ich habe die folgende Klasse:Grundlegendes zu C# -Generika und NULL-Wert. Return null oder NULL festlegbare

public class GenericClass<T> 
{ 
    public T Find() 
    { 
     //return T if found otherwise null or Nullable<T> 
    } 
} 

Irgendwo Ich möchte meine Klasse spezialisieren mit T mit einem class, ein anderes Mal mit einem struct. Ich bin mit dieser issue konfrontiert: Ich kann keine Nullable<T> zurückgeben, wenn T Typ nicht auf eine struct beschränkt ist.

Ich möchte eine Umsetzung meiner Find Verfahren bereitzustellen, das funktioniert, wenn T sowohl mit einem class oder struct spezialisiert. Falls Find fehlschlägt, würde ich gerne null zurückgeben, wenn T eine Klasse ist, sonst Nullable<T>.

Ist das ohne Reflektion möglich? Wenn ja wie?

+0

gehen Sie nicht T oder Nullable zurückkehren können, wenn der Rückgabetyp T. ist – fsimonazzi

Antwort

15

Sie können zurückgeben.

Für eine Klasse ist dies null. Für jede Nullable<T> ist dies eine Nullable<T> ohne einen Wert (effektiv null).

Wenn Sie dies mit einem struct und nicht einem Nullable<T> als Typ verwenden, wird default(T) der Standardwert der Struktur sein.


Wenn Sie diese Arbeit einheitlich für jede Klasse oder Struktur machen wollen, würden Sie wahrscheinlich brauchen zwei Werte zurückgeben - Sie den Rahmen als Inspiration nutzen könnten hier und haben eine TryXXX Methode, das heißt:

public bool TryFind(out T) 

Sie könnten dann default(T) verwenden, wenn der Wert nicht gefunden wird, und false zurückgeben. Dies vermeidet die Notwendigkeit für NULL-fähige Typen. Man könnte auch schreiben dies eine Tuple<bool, T> oder ähnliche Rückkehr, wenn Sie den out-Parameter, dh vermeiden wollte:

public Tuple<bool, T> Find() 

Eine letzte Option, möglicherweise wäre, um Ihre Klasse nicht-generic, dann verwenden Sie ein Paar generische Methoden:

class YourClass // Non generic 
{ 
    public T FindReference<T>() where T : class 
    { 
     // ... 
     return null; 
    } 

    public Nullable<T> FindValue<T>() where T : struct 
    { 
     // ... 
     return default(T); 
    } 
} 

Beachten Sie, dass verschiedene Namen benötigen, da Sie keine überladene Methode rein auf der Grundlage der Rückgabetyp haben kann.

+0

Technisch 'default (T)' keine 'Nullable zurückkehren' wenn 'int' wird als generisches Argument angegeben, wie das OP es wünscht, aber das bringt einen definitiv dorthin. Wahrhaftig, die einzige Möglichkeit, genau das zu erhalten, was das OP wünscht, wäre die Bereitstellung von zwei generischen Implementierungen, eine für Werttypen und eine für Referenztypen. – Tejs

+0

@Tejs. Wenn ich T mit dem Zahlentyp spezialisieren muss, muss ich dann den out-Parameter verwenden, um eine generische Lösung zu erreichen? Was gibt der Standardwert zurück, wenn T ein int ist? – Heisenbug

+0

@Tejs Ja - das war, was ich im letzten Satz sagen wollte - 'default (T)' liefert eine 'Nullable ', wenn Sie 'Nullable 'als generischen Typ verwenden, thoguh. –

2

würde ich die folgende Lösung verwenden:

public Boolean Find(out T result) 
{ 
    // Pseudo code 
    if (FindItem == true) 
    { 
     result = ItemFound; 
     return true; 
    } 
    result = default(T); 
    return false; 
} 

Warum? Weil Nullable<T> nur eine Struktur akzeptiert und die obige Methode sowohl Klassen als auch Strukturen unterstützt.

+1

+1, und dieses Idiom wird normalerweise 'TryXXXX' genannt, also wäre' TryGetValue' oder 'TryFind' geeignet. – user7116

+0

@sixlettervariables Ja, Sie haben Recht. Aber ich blieb bei dem Namen, den Heisenburg benutzte. –

0

Wie Reed sagte, könnten Sie default(T) zurückgeben.

Meiner Meinung nach hat dies einen großen Nachteil: Wenn Ihre Methode besagt, dass default(T) zurückgegeben wird, wenn das Element nicht gefunden wurde, verlieren Sie die Möglichkeit, die Standardwerte für Werttypen (z.die Rückgabe 0 für Find<int> könnte im Allgemeinen ein vollkommen gültiger Rückgabewert sein).

ich eher für so etwas wie diese

public class GenericClass<T> 
{ 
    public Option<T> Find() 
    { 
     //return Option.Some(item) if found otherwise Option.None<T>() 
    } 
} 

public static class Option 
{ 
    public static Option<T> None<T>() 
    { 
     return new Option<T>(default(T), false); 
    } 

    public static Option<T> Some<T>(T value) 
    { 
     return new Option<T>(value, true); 
    } 
} 

public sealed class Option<T> 
{ 
    private readonly T m_Value; 
    private readonly bool m_HasValue; 

    public void Option(T value, bool hasValue) 
    { 
     m_Value = value; 
     m_HasValue = hasValue; 
    } 

    public bool HasValue 
    { 
     get { return m_HasValue; } 
    }   

    public T Value 
    { 
     get { return m_Value; } 
    } 
} 
+1

Dies ist effektiv schreiben eine neue Form von 'Nullable ', die nicht auf Strukturen beschränkt ist - jetzt, wenn C# Mustererkennung hatte, wäre dies der Weg zu gehen;) –