Wie kann ich einen generischen Typ aus einer Zeichenfolgendarstellung abrufen?

79

Ich habe MyClass<T>.

Und dann habe ich das string s = "MyClass<AnotherClass>";. Wie kann ich Type aus der Zeichenfolge abrufen s?

Eine Möglichkeit (hässlich) besteht darin, das "<" und ">" zu analysieren und Folgendes zu tun:

Type acType = Type.GetType("AnotherClass");  
Type whatIwant = typeof (MyClass<>).MakeGenericType(acType);

Aber gibt es eine sauberere Möglichkeit, den endgültigen Typ ohne Analyse usw. zu erhalten?

DeeStackOverflow
quelle

Antworten:

94

Das Format für Generika ist der Name, ein `Zeichen, die Anzahl der Typparameter, gefolgt von einer durch Kommas getrennten Liste der Typen in Klammern:

Type.GetType("System.Collections.Generic.IEnumerable`1[System.String]");

Ich bin mir nicht sicher, ob es eine einfache Möglichkeit gibt, die C # -Syntax für Generika in die von der CLR gewünschte Zeichenfolge zu konvertieren. Ich habe angefangen, einen schnellen regulären Ausdruck zu schreiben, um ihn zu analysieren, wie Sie in der Frage erwähnt haben, aber mir wurde klar, dass das Parsen sehr kompliziert wird, wenn Sie nicht auf die Möglichkeit verzichten, verschachtelte Generika als Typparameter zu verwenden.

Neil Williams
quelle
+1 - tolle Antwort, danke! Ich habe herumgespielt und versucht herauszufinden, wie man mit Generika umgeht!
marc_s
Vielen Dank. Dies funktioniert und ich muss den Code ändern, um die Zeichenfolge auf diese Weise zu formatieren. Ich habe mich jedoch gefragt, ob es noch eine Möglichkeit gibt, einfach "MyClass <AnotherClass>" genau so zu verwenden, wie es in der Zeichenfolge angezeigt wird, um die Type-Instanz abzurufen. Sieht viel sauberer aus.
DeeStackOverflow
Sie müssen den Code nicht ändern, um die Zeichenfolge auf diese Weise zu formatieren. Rufen Sie einfach ToString () für den Typ auf.
Rush Frisby
38

Check out Activator.CreateInstance- Sie können es mit einem Typ aufrufen

Activator.CreateInstance(typeof(MyType))

oder mit einem Baugruppen- und Typnamen als string

Activator.CreateInstance("myAssembly", "myType")

Dadurch erhalten Sie eine Instanz des Typs, den Sie benötigen.

Wenn Sie Typedie Instanz anstelle der Instanz benötigen , verwenden Sie die Type.GetType()Methode und den vollständig qualifizierten Namen des Typs, an dem Sie interessiert sind, z.

string s = "System.Text.StringBuilder";
Type myClassType = Type.GetType(s);

Das gibt dir die TypeFrage.

marc_s
quelle
3
Dies erhält nur eine Instanz des Typs, keine System.Type-Instanz, die basierend auf dem Code-Snippet das zu sein scheint, wonach OP sucht.
Daniel Schaffer
26

Ich brauchte so etwas und schrieb schließlich Code, um die einfachen Typnamen zu analysieren, die ich brauchte. Natürlich gibt es Raum für Verbesserungen, da es nicht generische Typnamen wie identifizieren List<string>, aber es funktioniert gut für string, int[], decimal?und so weiter . Teilen, falls dies jemandem hilft.

public static class TypeExtensions
{
  public static Type GetTypeFromSimpleName(string typeName)
  {
    if (typeName == null)
      throw new ArgumentNullException("typeName");

    bool isArray = false, isNullable = false;

    if (typeName.IndexOf("[]") != -1)
    {
      isArray = true;
      typeName = typeName.Remove(typeName.IndexOf("[]"), 2);
    }

    if (typeName.IndexOf("?") != -1)
    {
      isNullable = true;
      typeName = typeName.Remove(typeName.IndexOf("?"), 1);
    }

    typeName = typeName.ToLower();

    string parsedTypeName = null;
    switch (typeName)
    {
      case "bool":
      case "boolean":
        parsedTypeName = "System.Boolean";
        break;
      case "byte":
        parsedTypeName = "System.Byte";
        break;
      case "char":
        parsedTypeName = "System.Char";
        break;
      case "datetime":
        parsedTypeName = "System.DateTime";
        break;
      case "datetimeoffset":
        parsedTypeName = "System.DateTimeOffset";
        break;
      case "decimal":
        parsedTypeName = "System.Decimal";
        break;
      case "double":
        parsedTypeName = "System.Double";
        break;
      case "float":
        parsedTypeName = "System.Single";
        break;
      case "int16":
      case "short":
        parsedTypeName = "System.Int16";
        break;
      case "int32":
      case "int":
        parsedTypeName = "System.Int32";
        break;
      case "int64":
      case "long":
        parsedTypeName = "System.Int64";
        break;
      case "object":
        parsedTypeName = "System.Object";
        break;
      case "sbyte":
        parsedTypeName = "System.SByte";
        break;
      case "string":
        parsedTypeName = "System.String";
        break;
      case "timespan":
        parsedTypeName = "System.TimeSpan";
        break;
      case "uint16":
      case "ushort":
        parsedTypeName = "System.UInt16";
        break;
      case "uint32":
      case "uint":
        parsedTypeName = "System.UInt32";
        break;
      case "uint64":
      case "ulong":
        parsedTypeName = "System.UInt64";
        break;
    }

    if (parsedTypeName != null)
    {
      if (isArray)
        parsedTypeName = parsedTypeName + "[]";

      if (isNullable)
        parsedTypeName = String.Concat("System.Nullable`1[", parsedTypeName, "]");
    }
    else
      parsedTypeName = typeName;

    // Expected to throw an exception in case the type has not been recognized.
    return Type.GetType(parsedTypeName);
  }
}

Die Verwendung ist so einfach wie das Schreiben:

Type t;

t = TypeExtensions.GetTypeFromSimpleName("string");
t = TypeExtensions.GetTypeFromSimpleName("int[]");
t = TypeExtensions.GetTypeFromSimpleName("decimal?");
Phillippe Santana
quelle
1
Kurz, perfekt, äußerst nützlich! Vielen Dank
xrnd
3

Verwenden Sie Folgendes, um nur das Typobjekt aus der Zeichenfolge abzurufen:

Type mytype = Type.GetType(typeName);

Sie können dies dann weitergeben an Activator.CreateInstance():

Activator.CreateInstance(mytype);
Turnor
quelle
0

Ich habe nicht viel Zeit, dies zu analysieren, obwohl ich glaube, ähnliche Antworten gesehen zu haben. Insbesondere denke ich, dass sie genau das tun, was Sie hier tun möchten:

Generischer Repository-Fehler im Entity Framework

(String.Format("[{0}]", baseType.Name.ToString())).OfType<T>();

Hoffentlich hilft das, lassen Sie mich genauer wissen, wenn dies nicht der Fall ist.

Jeff Ancel
quelle