Как я могу получить общий тип из строкового представления?

79

У меня есть MyClass<T>.

А потом у меня есть это string s = "MyClass<AnotherClass>";. Как я могу получить тип из строки s?

Один из способов (уродливый) - разобрать «<» и «>» и выполнить:

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

Но есть ли более чистый способ получить окончательный тип без синтаксического анализа и т.д.?

DeeStackOverflow
источник

Ответы:

94

Формат для воспроизведенных является имя, символ `, число параметров типа, за которым следует разделенный запятыми список типов в скобках:

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

Я не уверен, что есть простой способ преобразовать синтаксис C # для универсальных шаблонов в строку, которую хочет CLR. Я начал писать быстрое регулярное выражение, чтобы проанализировать его, как вы упомянули в вопросе, но понял, что, если вы не откажетесь от возможности иметь вложенные универсальные шаблоны в качестве параметров типа, синтаксический анализ станет очень сложным.

Нил Уильямс
источник
+1 - отличный ответ, спасибо! Я возился, пытаясь узнать, как обращаться с дженериками!
marc_s
Благодарю. Это работает, и мне нужно изменить код, чтобы отформатировать строку таким образом. Однако мне было интересно, есть ли еще способ просто использовать: «MyClass <AnotherClass>» точно так, как он показан в строке, чтобы получить экземпляр типа. Выглядит намного чище.
DeeStackOverflow
Вам не нужно изменять код для форматирования строки таким образом, просто вызовите ToString () для Type.
Rush Frisby
38

Отъезд Activator.CreateInstance- можно назвать типом

Activator.CreateInstance(typeof(MyType))

или с именем сборки и типа как string

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

Это даст вам экземпляр нужного вам типа.

Если вам нужен, Typeа не экземпляр, используйте Type.GetType()метод и полное имя интересующего вас типа, например:

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

Это даст вам Typeвопрос.

marc_s
источник
3
Это просто получает экземпляр типа, а не экземпляр System.Type, который на основе фрагмента кода, похоже, является тем, что ищет OP.
Дэниел Шаффер,
26

Мне нужно было что-то подобное, и в итоге я написал код для анализа нужных мне простых имен типов. Конечно , есть место для совершенствования, так как он не будет определять общие имена типов , такие как List<string>, но это не очень хорошо для string, int[], decimal?и тому подобное. Обмен на случай, если это кому-то поможет.

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

Использовать его так же просто, как написать это:

Type t;

t = TypeExtensions.GetTypeFromSimpleName("string");
t = TypeExtensions.GetTypeFromSimpleName("int[]");
t = TypeExtensions.GetTypeFromSimpleName("decimal?");
Филипп Сантана
источник
1
Коротко, идеально, чрезвычайно полезно! Спасибо
xrnd
3

Чтобы просто получить объект типа из строки, используйте:

Type mytype = Type.GetType(typeName);

Затем вы можете передать это Activator.CreateInstance():

Activator.CreateInstance(mytype);
Turnor
источник
0

У меня нет времени разбираться в этом, хотя я думаю, что видел некоторые похожие ответы. В частности, я думаю, что они делают именно то, что вы здесь хотите:

Ошибка общего репозитория Entity Framework

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

Надеюсь, это поможет, дайте мне знать более конкретно, если это не так.

Джефф Ансель
источник