Jak mogę uzyskać typ ogólny z reprezentacji ciągu?

79

Mam MyClass<T>.

A potem mam to string s = "MyClass<AnotherClass>";. Jak mogę uzyskać Type z ciągu s?

Jednym ze sposobów (brzydkim) jest przeanalizowanie „<” i „>” i wykonanie:

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

Ale czy istnieje bardziej przejrzysty sposób uzyskania ostatecznego typu bez analizowania itp.?

DeeStackOverflow
źródło

Odpowiedzi:

94

Format generyków to nazwa, od `charakter, liczba parametrów typu, po którym następuje lista oddzielonych przecinkami typów w nawiasach:

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

Nie jestem pewien, czy istnieje łatwy sposób przekonwertowania składni C # dla typów ogólnych na rodzaj ciągu, którego chce CLR. Zacząłem pisać szybkie wyrażenie regularne, aby przeanalizować je, tak jak wspomniałeś w pytaniu, ale zdałem sobie sprawę, że jeśli nie zrezygnujesz z zagnieżdżonych typów ogólnych jako parametrów typu, parsowanie będzie bardzo skomplikowane.

Neil Williams
źródło
+1 - świetna odpowiedź, dzięki! Bawiłem się, próbując dowiedzieć się, jak radzić sobie z generykami!
marc_s
Dzięki. To działa i muszę zmodyfikować kod, aby sformatować ciąg w ten sposób. Jednak zastanawiałem się, czy nadal istnieje sposób, aby po prostu użyć: „MyClass <AnotherClass>” dokładnie tak, jak jest to pokazane w ciągu, aby uzyskać instancję Type. Wygląda dużo czyściej.
DeeStackOverflow
Nie musisz modyfikować kodu, aby sformatować ciąg w ten sposób, po prostu wywołaj ToString () w Type.
Rush Frisby
38

Sprawdź Activator.CreateInstance- możesz to nazwać typem

Activator.CreateInstance(typeof(MyType))

lub z nazwą zestawu i typu jako string

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

W ten sposób uzyskasz instancję odpowiedniego typu.

Jeśli potrzebujesz Typeinstancji, a nie instancji, użyj Type.GetType()metody i w pełni kwalifikowanej nazwy typu, który Cię interesuje, np .:

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

To da ci Typepytanie.

marc_s
źródło
3
To po prostu dostaje instancję typu, a nie instancję System.Type, która na podstawie fragmentu kodu wydaje się być tym, czego szuka OP.
Daniel Schaffer
26

Potrzebowałem czegoś takiego i skończyło się na napisaniu kodu, aby przeanalizować proste nazwy typów, których potrzebowałem. Oczywiście istnieje możliwość poprawy, gdyż nie będą identyfikować nazwy rodzajowe typu jak List<string>, ale robi dobrze dla string, int[], decimal?i takie. Udostępnianie na wypadek, gdyby to komuś pomogło.

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);
  }
}

Używanie go jest tak proste, jak napisanie tego:

Type t;

t = TypeExtensions.GetTypeFromSimpleName("string");
t = TypeExtensions.GetTypeFromSimpleName("int[]");
t = TypeExtensions.GetTypeFromSimpleName("decimal?");
Phillippe Santana
źródło
1
Krótki, doskonały, niezwykle przydatny! Dzięki
xrnd
3

Aby po prostu pobrać obiekt typu z ciągu, użyj:

Type mytype = Type.GetType(typeName);

Następnie możesz przekazać to Activator.CreateInstance():

Activator.CreateInstance(mytype);
Turnor
źródło
0

Nie mam dużo czasu, aby to przeanalizować, chociaż wydaje mi się, że widziałem kilka podobnych odpowiedzi. W szczególności myślę, że robią dokładnie to, co chcesz tutaj zrobić:

Entity Framework Generic Repository Error

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

Mam nadzieję, że to pomoże. Jeśli tak nie jest, daj mi znać bardziej szczegółowo.

Jeff Ancel
źródło