web-dev-qa-db-fra.com

Comment puis-je obtenir un type générique à partir d'une représentation sous forme de chaîne?

J'ai MyClass<T>.

Et puis j'ai ce string s = "MyClass<AnotherClass>";. Comment puis-je obtenir Type à partir de la chaîne s?

Une façon (moche) est d'analyser les "<" et ">" et de faire:

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

Mais existe-t-il un moyen plus propre d'obtenir le type final sans analyse, etc.?

78
DeeStackOverflow

Le format pour les génériques est le nom, un caractère `, le nombre de paramètres de type, suivi d'une liste délimitée par des virgules des types entre parenthèses:

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

Je ne suis pas sûr qu'il existe un moyen facile de convertir de la syntaxe C # pour les génériques au type de chaîne que le CLR veut. J'ai commencé à écrire une expression rationnelle rapide pour l'analyser comme vous l'avez mentionné dans la question, mais j'ai réalisé qu'à moins que vous n'abandonniez la possibilité d'avoir des génériques imbriqués comme paramètres de type, l'analyse sera très compliquée.

93
Neil Williams

Découvrez Activator.CreateInstance - vous pouvez l'appeler avec un type

Activator.CreateInstance(typeof(MyType))

ou avec un assembly et tapez le nom comme string

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

Cela vous donnera une instance du type dont vous avez besoin.

Si vous avez besoin de Type plutôt que de l'instance, utilisez la méthode Type.GetType() et le nom complet du type qui vous intéresse, par exemple:

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

Cela vous donnera le Type en question.

38
marc_s

J'ai eu besoin de quelque chose comme ça et j'ai fini par écrire du code pour analyser les noms de type simples dont j'avais besoin. Bien sûr, il y a place à amélioration, car il n'identifiera pas les noms de type génériques comme List<string>, mais ça marche très bien pour string, int[], decimal? et autres choses de ce genre. Partager au cas où cela aiderait quelqu'un.

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

Son utilisation est aussi simple que d'écrire ceci:

Type t;

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

Pour obtenir simplement l'objet type de la chaîne, utilisez:

Type mytype = Type.GetType(typeName);

Vous pouvez ensuite transmettre ceci à Activator.CreateInstance():

Activator.CreateInstance(mytype);
3
Turnor

Je n'ai pas beaucoup de temps pour analyser cela, bien que je pense avoir vu des réponses similaires. En particulier, je pense qu'ils font exactement ce que vous voulez faire ici:

Erreur de référentiel générique Entity Framework

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

J'espère que cela aide, faites-moi savoir plus précisément si ce n'est pas le cas.

0
Jeff Ancel