Подтвердить что ты не робот

С# Получить общее имя типа

Мне нужно каким-то образом получить имя типа, когда type.IsGenericType= true.

    Type t = typeof(List<String>);
    MessageBox.Show( ..?.. );

Что я хочу, это окно сообщения, в котором отображается List... как я могу это сделать?

4b9b3361

Ответ 1

Type t = ...;

if (t.IsGenericType)
{
    Type g = t.GetGenericTypeDefinition();

    MessageBox.Show(g.Name);                                // displays "List`1"

    MessageBox.Show(g.Name.Remove(g.Name.IndexOf('`')));    // displays "List"
}

Ответ 2

Вы можете реализовать метод расширения, чтобы получить "понятное имя" типа, например так:

public static class TypeNameExtensions
{
    public static string GetFriendlyName(this Type type)
    {
        string friendlyName = type.Name;
        if (type.IsGenericType)
        {
            int iBacktick = friendlyName.IndexOf(''');
            if (iBacktick > 0)
            {
                friendlyName = friendlyName.Remove(iBacktick);
            }
            friendlyName += "<";
            Type[] typeParameters = type.GetGenericArguments();
            for (int i = 0; i < typeParameters.Length; ++i)
            {
                string typeParamName = GetFriendlyName(typeParameters[i]);
                friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
            }
            friendlyName += ">";
        }

        return friendlyName;
    }
}

С этим в вашем проекте, теперь вы можете сказать:

MessageBox.Show(t.GetFriendlyName());

И он будет отображать "Список <String>".

Я знаю, что OP не запрашивал параметры универсального типа, но я предпочитаю его таким образом. ;-)

Пространства имен, стандартные псевдонимы для встроенных типов и использование StringBuilder оставлены в качестве упражнения для читателя. ;-)

Ответ 3

Мой подход к йойо. Обеспечивает более дружественные имена для примитивов, обрабатывает массивы и рекурсивно обрабатывает вложенные дженерики. Также модульные тесты.

    private static readonly Dictionary<Type, string> _typeToFriendlyName = new Dictionary<Type, string>
    {
        { typeof(string), "string" },
        { typeof(object), "object" },
        { typeof(bool), "bool" },
        { typeof(byte), "byte" },
        { typeof(char), "char" },
        { typeof(decimal), "decimal" },
        { typeof(double), "double" },
        { typeof(short), "short" },
        { typeof(int), "int" },
        { typeof(long), "long" },
        { typeof(sbyte), "sbyte" },
        { typeof(float), "float" },
        { typeof(ushort), "ushort" },
        { typeof(uint), "uint" },
        { typeof(ulong), "ulong" },
        { typeof(void), "void" }
    };

    public static string GetFriendlyName(this Type type)
    {
        string friendlyName;
        if (_typeToFriendlyName.TryGetValue(type, out friendlyName))
        {
            return friendlyName;
        }

        friendlyName = type.Name;
        if (type.IsGenericType)
        {
            int backtick = friendlyName.IndexOf('`');
            if (backtick > 0)
            {
                friendlyName = friendlyName.Remove(backtick);
            }
            friendlyName += "<";
            Type[] typeParameters = type.GetGenericArguments();
            for (int i = 0; i < typeParameters.Length; i++)
            {
                string typeParamName = typeParameters[i].GetFriendlyName();
                friendlyName += (i == 0 ? typeParamName : ", " + typeParamName);
            }
            friendlyName += ">";
        }

        if (type.IsArray)
        {
            return type.GetElementType().GetFriendlyName() + "[]";
        }

        return friendlyName;
    }

[TestFixture]
public class TypeHelperTest
{
    [Test]
    public void TestGetFriendlyName()
    {
        Assert.AreEqual("string", typeof(string).FriendlyName());
        Assert.AreEqual("int[]", typeof(int[]).FriendlyName());
        Assert.AreEqual("int[][]", typeof(int[][]).FriendlyName());
        Assert.AreEqual("KeyValuePair<int, string>", typeof(KeyValuePair<int, string>).FriendlyName());
        Assert.AreEqual("Tuple<int, string>", typeof(Tuple<int, string>).FriendlyName());
        Assert.AreEqual("Tuple<KeyValuePair<object, long>, string>", typeof(Tuple<KeyValuePair<object, long>, string>).FriendlyName());
        Assert.AreEqual("List<Tuple<int, string>>", typeof(List<Tuple<int, string>>).FriendlyName());
        Assert.AreEqual("Tuple<short[], string>", typeof(Tuple<short[], string>).FriendlyName());
    }
}

Ответ 5

public static class TypeNameExtensions
{
    public static string GetFriendlyName(this Type type)
    {
        var friendlyName = type.Name;
        if (!type.IsGenericType) return friendlyName;

        var iBacktick = friendlyName.IndexOf('`');
        if (iBacktick > 0) friendlyName = friendlyName.Remove(iBacktick);

        var genericParameters = type.GetGenericArguments().Select(x => x.GetFriendlyName());
        friendlyName += "<" + string.Join(", ", genericParameters) + ">";

        return friendlyName;
    }
}

Ответ 6

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

public static string GetFriendlyName(this Type type)
{
    if (type.IsGenericType)
    {
        var name = type.Name.Substring(0, type.Name.IndexOf('`'));
        var types = string.Join(",", type.GetGenericArguments().Select(GetFriendlyName));
        return $"{name}<{types}>";
    }
    else
    {
        return type.Name;
    }
}

Ответ 7

У меня улучшена версия yoyos для использования в Code Generation. Обратите внимание, что теперь все типы ссылаются на полную квалификацию = > global:: System.String.

            public static string GetFriendlyTypeName(Type type)
            {
                string friendlyName = type.Name;
                if (type.IsGenericType)
                {
                    int iBacktick = friendlyName.IndexOf('`');
                    if (iBacktick > 0)
                    {
                        friendlyName = friendlyName.Remove(iBacktick);
                    }
                    friendlyName += "<";
                    Type[] typeParameters = type.GetGenericArguments();
                    for (int i = 0; i < typeParameters.Length; ++i)
                    {
                        string typeParamName = GetFriendlyTypeName(typeParameters[i]);
                        friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
                    }
                    friendlyName += ">";
                    friendlyName = "global::" + type.Namespace + "." + friendlyName;
                }
                else
                {
                    friendlyName = "global::" + type.FullName;
                }

                return friendlyName.Replace('+', '.');
            }

Ответ 8

Я знаю, что это старый вопрос, но мне и коллеге нужно было сделать это для какой-то интеллигентной/розлинской работы. Оптимальным решением оказалось решение Али, но оно не работает для вложенных типов:

    int i = 1; //would work
    List<string> listTest = new List<string>(); //would work
    Dictionary<string, int> dictTest = new Dictionary<string, int>(); //would work
    Dictionary<int, List<string>> nestTest = new Dictionary<int, List<string>>(); //would fail
    Dictionary<int, List<Dictionary<string, List<object>>>> superNestTest = new Dictionary<int, List<Dictionary<string, List<object>>>>(); //would fail
    Dictionary<int, List<Dictionary<string, int>>> superNestTest2 = new Dictionary<int, List<Dictionary<string, int>>>(); //would fail

Чтобы решить эти проблемы, я преобразовал функцию в рекурсивный метод:

public static class TypeExtensions
{
    public static string GetFriendlyName(this Type type)
    {
        string friendlyName = type.FullName;
        if (type.IsGenericType)
        {
            friendlyName = GetTypeString(type);
        }
        return friendlyName;
    }

    private static string GetTypeString(Type type)
    {
        var t = type.AssemblyQualifiedName;

        var output = new StringBuilder();
        List<string> typeStrings = new List<string>();  

        int iAssyBackTick = t.IndexOf(''') + 1;
        output.Append(t.Substring(0, iAssyBackTick - 1).Replace("[", string.Empty));
        var genericTypes = type.GetGenericArguments();

        foreach (var genType in genericTypes)
        {
            typeStrings.Add(genType.IsGenericType ? GetTypeString(genType) : genType.ToString());
        }

        output.Append($"<{string.Join(",", typeStrings)}>");
        return output.ToString();
    }
}

выполнение предыдущих примеров/тестовых примеров дало следующие результаты:

System.Int32
System.Collections.Generic.List<System.String>
System.Collections.Generic.Dictionary<System.String,System.Int32>
System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.String>>
System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String,System.Collections.Generic.List<System.Object>>>>
System.Collections.Generic.Dictionary<System.Int32,System.Collections.Generic.List<System.Collections.Generic.Dictionary<System.String,System.Int32>>>

Я потратил некоторое время, пытаясь решить проблему с вложенными типами, поэтому хотел документировать это здесь, чтобы кто-нибудь еще в будущем мог сэкономить немало времени (и головных болей!). Я также проверил производительность, и это в микросекундах для завершения (8 микросекунд в случае последнего сценария:

Результаты производительности
(Имена переменных, используемые из оригинального списка сценариев)
"i" | 43uS
"listTest" | 3uS
"dictTest" | 2uS
"nestTest" | 5uS
"superNestTest" | 9uS
"superNestTest2" | 9uS
Среднее количество раз после выполнения вышеуказанного кода 200 раз для каждого сценария