Der beste Weg, um zu testen, ob ein generischer Typ eine Zeichenfolge ist? (C #)

93

Ich habe eine generische Klasse, die jeden Typ zulassen sollte, primitiv oder auf andere Weise. Das einzige Problem dabei ist die Verwendung default(T). Wenn Sie für einen Werttyp oder eine Zeichenfolge die Standardeinstellung aufrufen, wird diese auf einen angemessenen Wert (z. B. eine leere Zeichenfolge) initialisiert. Wenn Sie default(T)ein Objekt aufrufen , wird null zurückgegeben. Aus verschiedenen Gründen müssen wir sicherstellen, dass wir eine Standardinstanz des Typs haben, die nicht null ist, wenn es sich nicht um einen primitiven Typ handelt . Hier ist Versuch 1:

T createDefault()
{
    if(typeof(T).IsValueType)
    {
        return default(T);
    }
    else
    {
        return Activator.CreateInstance<T>();
    }
}

Problem - Zeichenfolge ist kein Werttyp, hat jedoch keinen parameterlosen Konstruktor. Die aktuelle Lösung lautet also:

T createDefault()
{
    if(typeof(T).IsValueType || typeof(T).FullName == "System.String")
    {
        return default(T);
    }
    else
    {
        return Activator.CreateInstance<T>();
    }
}

Aber das fühlt sich an wie ein Kludge. Gibt es eine schönere Möglichkeit, mit dem String-Fall umzugehen?

Rex M.
quelle

Antworten:

160

Beachten Sie, dass der Standardwert (Zeichenfolge) null und nicht string.Empty ist. Möglicherweise möchten Sie einen Sonderfall in Ihrem Code:

if (typeof(T) == typeof(String)) return (T)(object)String.Empty;
Matt Hamilton
quelle
2
Ich dachte, ich hätte diese Lösung früher ausprobiert und sie hat nicht funktioniert, aber ich muss etwas Dummes getan haben. Und danke, dass Sie darauf hingewiesen haben, dass default (string) null zurückgibt. Wir sind deshalb noch nicht auf einen Fehler gestoßen, aber das ist wahr.
Rex M
1
@Matt Hamilton: +1, aber Sie sollten Ihre Antwort aktualisieren, um '(T) (Objekt) String.Empty' zurückzugeben, wie von CodeInChaos vorgeschlagen, da der Rückgabetyp der Methode generisch ist. Sie können nicht nur einen String zurückgeben.
VoodooChild
2
Was ist mit dem isSchlüsselwort? Ist das hier nicht von Nutzen?
Naveed Butt
Im Moment ist es nicht möglich, den is-Operator mit Generika und Zuordnung oder direkter Instanzierung anzuwenden, nicht wahr? Wird eine coole Funktion sein
Juan Pablo Garcia Coello
14
if (typeof(T).IsValueType || typeof(T) == typeof(String))
{
     return default(T);
}
else
{
     return Activator.CreateInstance<T>();
}

Ungetestet, aber das erste, was mir in den Sinn kam.

FlySwat
quelle
4

Sie können die TypeCode- Aufzählung verwenden. Rufen Sie die GetTypeCode-Methode für Klassen auf, die die IConvertible-Schnittstelle implementieren, um den Typcode für eine Instanz dieser Klasse abzurufen. IConvertible wird von Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char und String implementiert, sodass Sie damit nach primitiven Typen suchen können. Weitere Informationen zu " Generic Type Checking ".

jfs
quelle
2

Persönlich mag ich Methodenüberladung:

public static class Extensions { 
  public static String Blank(this String me) {      
    return String.Empty;
  }
  public static T Blank<T>(this T me) {      
    var tot = typeof(T);
    return tot.IsValueType
      ? default(T)
      : (T)Activator.CreateInstance(tot)
      ;
  }
}
class Program {
  static void Main(string[] args) {
    Object o = null;
    String s = null;
    int i = 6;
    Console.WriteLine(o.Blank()); //"System.Object"
    Console.WriteLine(s.Blank()); //""
    Console.WriteLine(i.Blank()); //"0"
    Console.ReadKey();
  }
}
Theoski
quelle
-6

Die Diskussion für String funktioniert hier nicht.

Ich musste folgenden Code für Generika haben, damit es funktioniert -

   private T createDefault()
    { 

        {     
            if(typeof(T).IsValueType)     
            {         
                return default(T);     
            }
            else if (typeof(T).Name == "String")
            {
                return (T)Convert.ChangeType(String.Empty,typeof(T));
            }
            else
            {
                return Activator.CreateInstance<T>();
            } 
        } 

    }
Anil
quelle
3
Das Testen nach StringNamen, insbesondere ohne Berücksichtigung eines Namespace, ist schlecht. Und ich mag es auch nicht, wie du konvertierst.
CodesInChaos