Получить GenericType-Name в хорошем формате, используя Reflection на C # - PullRequest
8 голосов
/ 07 октября 2009

Мне нужно получить имя универсального типа в форме его объявления в коде.

Например: Для List я хочу получить строку «List ». Стандартное свойство Type.Name возвращает «List`1» в этой ситуации.

РЕДАКТИРОВАТЬ: пример был исправлен

Ответы [ 7 ]

13 голосов
/ 13 августа 2014

Используя встроенные функции и Linq, это можно написать

static string PrettyTypeName(Type t)
{
    if (t.IsGenericType)
    {
        return string.Format(
            "{0}<{1}>",
            t.Name.Substring(0, t.Name.LastIndexOf("`", StringComparison.InvariantCulture)),
            string.Join(", ", t.GetGenericArguments().Select(PrettyTypeName)));
    }

    return t.Name;
}

ПРИМЕЧАНИЕ. В более старой версии C # компилятору требуется явное .ToArray():

            string.Join(", ", t.GetGenericArguments().Select(PrettyTypeName).ToArray()));
11 голосов
/ 07 октября 2009

Хорошо, я провёл кучу исследований и обнаружил, что typeof (List) имеет «GetGenericArguments», который получит вам вложенные имена. Таким образом, я бы сделал это так (для 1 универсального типа, если это мульти, это займет цикл или что-то в этом роде. Я могу опубликовать функцию для этого, если потребуется.

Вот функция, которая делает это с несколькими универсальными аргументами, обрабатывает «вложенные» универсальные типы. Отредактировано снова, чтобы использовать эту функцию Aggregate:

static string GetFullName(Type t)
{
    if (!t.IsGenericType)
        return t.Name;
    StringBuilder sb=new StringBuilder();

    sb.Append(t.Name.Substring(0, t.Name.LastIndexOf("`")));
    sb.Append(t.GetGenericArguments().Aggregate("<",

        delegate(string aggregate,Type type)
            {
                return aggregate + (aggregate == "<" ? "" : ",") + GetFullName(type);
            }  
        ));
    sb.Append(">");

    return sb.ToString();
}
3 голосов
/ 07 октября 2009

Это не так уж сложно. ; -)

Хорошо, я укушу ... g Приведенный ниже код работает рекурсивно и отображает примитивные типы без пространства имен (как писал OP):

  static string PrettyPrintGenericTypeName(Type typeRef)
  {
     var rootType = typeRef.IsGenericType
        ? typeRef.GetGenericTypeDefinition()
        : typeRef;

     var cleanedName = rootType.IsPrimitive
                          ? rootType.Name
                          : rootType.ToString();

     if (!typeRef.IsGenericType)
        return cleanedName;
     else
        return cleanedName.Substring(0,
                                     cleanedName.LastIndexOf('`'))
            + typeRef.GetGenericArguments()
                     .Aggregate("<",
                                (r, i) =>
                                   r
                                   + (r != "<" ? ", " : null)
                                   + PrettyPrintGenericTypeName(i))
            + ">";
  }

Полученное значение cleanedName выглядит следующим образом: System.Collections.Generic.Dictionary<System.Collections.Generic.List<Int32>, ConsoleApplication2.Program+SomeType>

0 голосов
/ 07 марта 2019

Если вы не удалите имена пространств имен, просто скажите:

Regex.Replace(""+@type, @"`\d+\[", "<").Replace("]", ">");

и, если вы это сделаете, скажите:

Regex.Replace(Regex.Replace(""+@type, @"`\d+\[", "<").Replace("]", ">"), @"\w+\.", "")
0 голосов
/ 14 декабря 2016

Старый вопрос, но мне это нужно только сегодня. Поэтому я написал метод расширения, который может выводить красивое C # -форматированное универсальное имя, которое может обрабатывать многоуровневые вложенные универсальные типы.

using System;
using System.Text;

public static class TypeExtensions
{
    public static string GetNiceName(this Type type, bool useFullName = false)
    {
        if (!type.IsGenericType) {
            return type.Name;
        }

        var typeNameBuilder = new StringBuilder();
        GetNiceGenericName(typeNameBuilder, type, useFullName);
        return typeNameBuilder.ToString();
    }

    static void GetNiceGenericName(StringBuilder sb, Type type, bool useFullName)
    {
        if (!type.IsGenericType) {
            sb.Append(useFullName ? type.FullName : type.Name);
            return;
        }

        var typeDef = type.GetGenericTypeDefinition();
        var typeName = useFullName ? typeDef.FullName : typeDef.Name;
        sb.Append(typeName);
        sb.Length -= typeName.Length - typeName.LastIndexOf('`');
        sb.Append('<');
        foreach (var typeArgument in type.GenericTypeArguments) {
            GetNiceGenericName(sb, typeArgument, useFullName);
            sb.Append(", ");
        }
        sb.Length -= 2;
        sb.Append('>');
    }
}
0 голосов
/ 25 мая 2016

Еще один пример, который я написал сам перед тем, как спотыкаться здесь.

    private string PrettyPrintGenericTypeName(Type p)
    {
        if (p.IsGenericType) {
            var simpleName = p.Name.Substring(0, p.Name.IndexOf('`'));
            var genericTypeParams = p.GenericTypeArguments.Select(PrettyPrintGenericTypeName).ToList();
            return string.Format("{0}<{1}>", simpleName, string.Join(", ", genericTypeParams));
        } else {
            return p.Name;
        }
    }
0 голосов
/ 07 октября 2009

Ну, это потому, что имя типа в .NET на самом деле IS List'1. «1» - это так называемая арность обобщенного типа, и она сообщает, сколько существует параметров типа.

Это необходимо для того, чтобы вы могли создать более 1 универсального типа с тем же «именем», но с другим числом параметров универсального типа.

Например, существует более 1 типа, называемого System.Action. Настоящие имена - System.Action'1, System.Action'2, System.Action'3 и т. Д.

Итак, если вы знаете, что ваш тип является общим, вы можете предположить, что в конце имени стоит этот 'XX, так что вы можете просто вырезать эту часть, например, так:

string strTypeName = typeof(List<>).Name.Substring(0, typeof(List<>).Name.LastIndexOf("`"));

PS: Пожалуйста, замените 'на `.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...