C #: «красивая» функция имени типа? - PullRequest
18 голосов
/ 19 июня 2011

Свойства имени класса System.Type возвращают странный результат в случае универсальных типов.Есть ли способ получить имя типа в формате ближе к тому, как я его указал?Пример: typeof(List<string>).OriginalName == "List<string>"

Ответы [ 11 ]

29 голосов
/ 19 июня 2011

Проблема с «красивыми» именами в том, что они различаются в зависимости от языка, который вы используете. Представьте себе удивление разработчика VB.NET, если OriginalName вернул синтаксис C #.

Однако сделать это самостоятельно довольно просто:

private static string PrettyName(Type type)
{
    if (type.GetGenericArguments().Length == 0)
    {
        return type.Name;
    }
    var genericArguments = type.GetGenericArguments();
    var typeDefeninition = type.Name;
    var unmangledName = typeDefeninition.Substring(0, typeDefeninition.IndexOf("`"));
    return unmangledName + "<" + String.Join(",", genericArguments.Select(PrettyName)) + ">";
}

Это рекурсивно разрешит неуправляемое имя, так что если у вас есть что-то вроде Dictionary<string, IList<string>>, оно все равно будет работать.

15 голосов
/ 19 июня 2011

Я использовал CodeDomProvider для преобразования в c #:

    public static string GetOriginalName(this Type type)
    {
        string TypeName = type.FullName.Replace(type.Namespace + ".", "");//Removing the namespace

        var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); //You can also use "VisualBasic"
        var reference = new System.CodeDom.CodeTypeReference(TypeName);

        return provider.GetTypeOutput(reference);
    }
2 голосов
/ 07 августа 2015

Вот моя реализация. Он был создан для описания методов, поэтому он обрабатывает ключевые слова ref и out.

private static Dictionary<Type, string> shorthandMap = new Dictionary<Type, string>
{
    { typeof(Boolean), "bool" },
    { typeof(Byte), "byte" },
    { typeof(Char), "char" },
    { typeof(Decimal), "decimal" },
    { typeof(Double), "double" },
    { typeof(Single), "float" },
    { typeof(Int32), "int" },
    { typeof(Int64), "long" },
    { typeof(SByte), "sbyte" },
    { typeof(Int16), "short" },
    { typeof(String), "string" },
    { typeof(UInt32), "uint" },
    { typeof(UInt64), "ulong" },
    { typeof(UInt16), "ushort" },
};

private static string CSharpTypeName(Type type, bool isOut = false)
{
    if (type.IsByRef)
    {
        return String.Format("{0} {1}", isOut ? "out" : "ref", CSharpTypeName(type.GetElementType()));
    }
    if (type.IsGenericType)
    {
        if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            return String.Format("{0}?", CSharpTypeName(Nullable.GetUnderlyingType(type)));
        }
        else
        {
            return String.Format("{0}<{1}>", type.Name.Split('`')[0],
                String.Join(", ", type.GenericTypeArguments.Select(a => CSharpTypeName(a)).ToArray()));
        }
    }
    if (type.IsArray)
    {
        return String.Format("{0}[]", CSharpTypeName(type.GetElementType()));
    }

    return shorthandMap.ContainsKey(type) ? shorthandMap[type] : type.Name;
}

Код вызова выглядит следующим образом:

string line = String.Format("{0}.{1}({2})",
    method.DeclaringType.Name,
    method.Name,
    String.Join(", ", method.GetParameters().Select(p => CSharpTypeName(p.ParameterType, p.IsOut) + " " + p.Name).ToArray()));

Где method - это MethodInfo экземпляр.

Одно замечание: у меня не было необходимости описывать какие-либо типы многомерных массивов, поэтому я не стал реализовывать описание для этого, но добавить его было бы довольно просто:

2 голосов
/ 22 января 2014

Как и в случае с Гарольдом Хойером, но включая nullables и еще несколько встроенных типов:

/// <summary>
/// Get full type name with full namespace names
/// </summary>
/// <param name="type">
/// The type to get the C# name for (may be a generic type or a nullable type).
/// </param>
/// <returns>
/// Full type name, fully qualified namespaces
/// </returns>
public static string CSharpName(this Type type)
{
    Type nullableType = Nullable.GetUnderlyingType(type);
    string nullableText;
    if (nullableType != null)
    {
        type = nullableType;
        nullableText = "?";
    }
    else
    {
        nullableText = string.Empty;
    }

    if (type.IsGenericType)
    {
        return string.Format(
            "{0}<{1}>{2}", 
            type.Name.Substring(0, type.Name.IndexOf('`')), 
            string.Join(", ", type.GetGenericArguments().Select(ga => ga.CSharpName())), 
            nullableText);
    }

    switch (type.Name)
    {
        case "String":
            return "string";
        case "Int32":
            return "int" + nullableText;
        case "Decimal":
            return "decimal" + nullableText;
        case "Object":
            return "object" + nullableText;
        case "Void":
            return "void" + nullableText;
        default:
            return (string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName) + nullableText;
    }
}
2 голосов
/ 19 июня 2011

Вы должны написать это сами.Помните, что Type.Name и т. Д. Вызывают методы, которые существуют в CLR и могут вызываться из нескольких языков.Вот почему они не возвращаются в виде C # или VB или языка, на котором кодировщик был закодирован, а вместо этого выглядят как представление CLR.

Обратите внимание, что string и что не является псевдонимами для CLRтипы как System.String.Опять же, это играет роль в форматировании, которое вы видите.

Использовать отражение несложно, но я сомневаюсь в его значении.

1 голос
/ 11 января 2018

Минимальное рабочее решение, которое использует CodeDomProvider, - это управление тем, как в первую очередь создается экземпляр CodeTypeReference. Существуют особые случаи только для универсальных типов и массивов с несколькими рангами, поэтому мы должны заботиться только о них:

static CodeTypeReference CreateTypeReference(Type type)
{
    var typeName = (type.IsPrimitive || type == typeof(string)) ? type.FullName : type.Name;
    var reference = new CodeTypeReference(typeName);
    if (type.IsArray)
    {
        reference.ArrayElementType = CreateTypeReference(type.GetElementType());
        reference.ArrayRank = type.GetArrayRank();
    }

    if (type.IsGenericType)
    {
        foreach (var argument in type.GetGenericArguments())
        {
            reference.TypeArguments.Add(CreateTypeReference(argument));
        }
    }
    return reference;
}

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

using (var provider = new CSharpCodeProvider())
{
    var reference = CreateTypeReference(typeof(IObservable<IEnumerable<Tuple<int?, string>>>[,]));
    var output = provider.GetTypeOutput(reference);
    Console.WriteLine(output);
}

Код выше возвращает IObservable<IEnumerable<Tuple<Nullable<int>, string>>>[,]. Единственный особый случай, который не обрабатывается должным образом, - это Nullable типы, но на самом деле это скорее ошибка CodeDomProvider, чем все остальное.

0 голосов
/ 23 мая 2019

Я делаю это так ..

public static class TypeExtensions {
    public static String GetName(this Type t) {
        String readable;

#if PREVENT_RECURSION
        if(m_names.TryGetValue(t, out readable)) {
            return readable;
        }
#endif

        var tArgs = t.IsGenericType ? t.GetGenericArguments() : Type.EmptyTypes;
        var name = t.Name;
        var format = Regex.Replace(name, @"`\d+.*", "")+(t.IsGenericType ? "<?>" : "");
        var names = tArgs.Select(x => x.IsGenericParameter ? "" : GetName(x));
        readable=String.Join(String.Join(",", names), format.Split('?'));

#if PREVENT_RECURSION
        m_names.Add(t, readable);
#endif

        return readable;
    }

    static readonly Dictionary<Type, String> m_names = new Dictionary<Type, String> { };
}

где PREVENT_RECURSION должно быть определено true, если вы не хотите, чтобы тип в аргументах универсального типа каждый раз разрешался рекурсивно, просто возьмите его из словаря кэша; оставьте неопределенным значение false, если вам все равно.

0 голосов
/ 09 февраля 2015

Во-первых: Престижность Navid для предотвращения повторного изобретения колес Я бы проголосовал, если бы мог.

Вот несколько моментов, которые нужно добавить, если кто-то пойдет по этому пути (по крайней мере, для VS10 / .Net 4):
* Попробуйте использовать один из вариантов CodeTypeReference с аргументами типа. Это позволяет избежать ряда ловушек при использовании имен строковых типов (таких как трейлинг &) и означает, что вы получаете bool вместо System.Boolean и т. Д. Вы возвращаете полное пространство имен для многих типов, подобных этому, но вы всегда можете избавиться от части пространства имен позже.
* Простые Nullables имеют тенденцию возвращаться в форме System.Nullable<int>, а не int? - Вы можете преобразовать в более хороший синтаксис с помощью регулярного выражения в ответе, например: const string NullablePattern = @"System.Nullable<(?<nulledType>[\w\.]+)>"; const string NullableReplacement = @"${nulledType}?"; answer = Regex.Replace(answer, NullablePattern, NullableReplacement);
* Тип аргумента метода, например out T?, дает очень непрозрачную строку. Если у кого-то есть элегантный способ иметь дело с такими вещами, я бы хотел узнать об этом.

Надеюсь, с Рослином все это станет очень легко.

0 голосов
/ 29 июля 2011

Я понял, что должен написать сам.Вот мое решение (на самом деле это немного больше, чем просили).Это, вероятно, полезно.

using System.Reflection;
using HWClassLibrary.Debug;
using System.Collections.Generic;
using System.Linq;
using System;

namespace HWClassLibrary.Helper
{
    public static class TypeNameExtender
    {
        private static IEnumerable<Type> _referencedTypesCache;

        public static void OnModuleLoaded() { _referencedTypesCache = null; }

        public static string PrettyName(this Type type)
        {
            if(type == typeof(int))
                return "int";
            if(type == typeof(string))
                return "string";

            var result = PrettyTypeName(type);
            if(type.IsGenericType)
                result = result + PrettyNameForGeneric(type.GetGenericArguments());
            return result;
        }

        private static string PrettyTypeName(Type type)
        {
            var result = type.Name;
            if(type.IsGenericType)
                result = result.Remove(result.IndexOf('`'));

            if (type.IsNested && !type.IsGenericParameter)
                return type.DeclaringType.PrettyName() + "." + result;

            if(type.Namespace == null)
                return result;

            var conflictingTypes = ReferencedTypes
                .Where(definedType => definedType.Name == type.Name && definedType.Namespace != type.Namespace)
                .ToArray();

            var namespaceParts = type.Namespace.Split('.').Reverse().ToArray();
            var namespacePart = "";
            for(var i = 0; i < namespaceParts.Length && conflictingTypes.Length > 0; i++)
            {
                namespacePart = namespaceParts[i] + "." + namespacePart;
                conflictingTypes = conflictingTypes
                    .Where(conflictingType => (conflictingType.Namespace + ".").EndsWith(namespacePart))
                    .ToArray();
            }

            return namespacePart + result;
        }

        private static IEnumerable<Type> ReferencedTypes
        {
            get
            {
                if(_referencedTypesCache == null)
                    _referencedTypesCache = Assembly.GetEntryAssembly().GetReferencedTypes();
                return _referencedTypesCache;
            }
        }

        private static string PrettyNameForGeneric(Type[] types)
        {
            var result = "";
            var delim = "<";
            foreach(var t in types)
            {
                result += delim;
                delim = ",";
                result += t.PrettyName();
            }
            return result + ">";
        }
    }
}
0 голосов
/ 19 июня 2011

Я понимаю, что вы хотите сравнить типы.
Лучший способ сделать это ...
myVar is List<string> или
myVar.GetType() == myOtherVar.GetType()

Если вам это не нужно ... не обращайте внимания на мой ответ.

...