¿Cómo puedo obtener un tipo genérico a partir de una representación de cadena?


79

Tengo MyClass<T>.

Y luego tengo esto string s = "MyClass<AnotherClass>";. ¿Cómo puedo obtener Type de la cadena s?

Una forma (fea) es analizar "<" y ">" y hacer:

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

Pero, ¿hay una forma más limpia de obtener el tipo final sin analizar, etc.?

Respuestas:


94

El formato de los genéricos es el nombre, un carácter `, el número de parámetros de tipo, seguido de una lista delimitada por comas de los tipos entre paréntesis:

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

No estoy seguro de que haya una manera fácil de convertir de la sintaxis C # para genéricos al tipo de cadena que CLR quiere. Comencé a escribir una expresión regular rápida para analizarlo como mencionaste en la pregunta, pero me di cuenta de que a menos que renuncies a la capacidad de tener genéricos anidados como parámetros de tipo, el análisis se volverá muy complicado.


+1 - gran respuesta, ¡gracias! ¡Estaba jugueteando tratando de averiguar cómo manejar los genéricos!
marc_s

Gracias. Esto funciona y tengo que modificar el código para formatear la cadena de esta manera. Sin embargo, me preguntaba si todavía hay una manera de usar simplemente: "MyClass <AnotherClass>" exactamente como se muestra en la cadena para obtener la instancia de Type. Parece mucho más limpio.
DeeStackOverflow

No tiene que modificar el código para formatear la cadena de esta manera, simplemente llame a ToString () en el Tipo.
Rush Frisby

38

Mira Activator.CreateInstance, puedes llamarlo con un tipo

Activator.CreateInstance(typeof(MyType))

o con un ensamblado y un nombre de tipo como string

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

Esto le dará una instancia del tipo que necesita.

Si necesitas el Type lugar de la instancia, use el Type.GetType()método y el nombre completo del tipo que le interesa, por ejemplo:

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

Eso te dará la Typepregunta en cuestión.


3
Esto solo obtiene una instancia del tipo, no una instancia System.Type, que según el fragmento de código, parece ser lo que OP está buscando.
Daniel Schaffer

26

Necesitaba algo como esto y terminé escribiendo un código para analizar los nombres de tipos simples que necesitaba. Por supuesto, hay margen de mejora, ya que no identifica los nombres de tipos genéricos como List<string>, pero lo hace muy bien para string, int[], decimal?y tal. Compartir en caso de que esto ayude a alguien.

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

Usarlo es tan simple como escribir esto:

Type t;

t = TypeExtensions.GetTypeFromSimpleName("string");
t = TypeExtensions.GetTypeFromSimpleName("int[]");
t = TypeExtensions.GetTypeFromSimpleName("decimal?");

1
¡Corto, perfecto, extremadamente útil! Gracias
xrnd

3

Para obtener el objeto de tipo de la cadena, use:

Type mytype = Type.GetType(typeName);

Luego puede pasar esto a Activator.CreateInstance():

Activator.CreateInstance(mytype);

0

No tengo mucho tiempo para analizar esto, aunque creo que he visto algunas respuestas similares. En particular, creo que están haciendo exactamente lo que quieres hacer aquí:

Error de repositorio genérico de Entity Framework

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

Espero que esto ayude, avíseme más específicamente si esto no es así.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.