Get user-friendly name for generic type in C#

前端 未结 5 1585
自闭症患者
自闭症患者 2020-12-06 00:32

Is there an easy way without writing a recursive method which will give a \'user friendly\' name for a generic type from the Type class?

E.g. For the fo

相关标签:
5条回答
  • 2020-12-06 01:03

    Building a more complete answer off of Kirk's would look like this. Modifications:

    • Support for all C# keywords
    • Supports custom-translations
    • Arrays
    • Nullables being ValueType? instead of Nullable<ValueType>

    Here's full code:

    public static class TypeTranslator
    {
        private static Dictionary<Type, string> _defaultDictionary = new Dictionary<System.Type, string>
        {
            {typeof(int), "int"},
            {typeof(uint), "uint"},
            {typeof(long), "long"},
            {typeof(ulong), "ulong"},
            {typeof(short), "short"},
            {typeof(ushort), "ushort"},
            {typeof(byte), "byte"},
            {typeof(sbyte), "sbyte"},
            {typeof(bool), "bool"},
            {typeof(float), "float"},
            {typeof(double), "double"},
            {typeof(decimal), "decimal"},
            {typeof(char), "char"},
            {typeof(string), "string"},
            {typeof(object), "object"},
            {typeof(void), "void"}
        };
    
        public static string GetFriendlyName(this Type type, Dictionary<Type, string> translations)
        {
            if(translations.ContainsKey(type))
                return translations[type];
            else if (type.IsArray)
            {
                var rank = type.GetArrayRank();
                var commas = rank > 1 
                    ? new string(',', rank - 1)
                    : "";
                return GetFriendlyName(type.GetElementType(), translations) + $"[{commas}]";
            }
            else if(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                return type.GetGenericArguments()[0].GetFriendlyName() + "?";
            else if (type.IsGenericType)
                return type.Name.Split('`')[0] + "<" + string.Join(", ", type.GetGenericArguments().Select(x => GetFriendlyName(x)).ToArray()) + ">";
            else
                return type.Name;
        }
    
        public static string GetFriendlyName(this Type type)
        {
            return type.GetFriendlyName(_defaultDictionary);
        }
    }
    
    0 讨论(0)
  • 2020-12-06 01:08

    You can avoid writing a recursive method by calling the recursive method that's already provided for you:

    static string GetTypeName(Type type)
    {
        var codeDomProvider = CodeDomProvider.CreateProvider("C#");
        var typeReferenceExpression = new CodeTypeReferenceExpression(new CodeTypeReference(type));
        using (var writer = new StringWriter())
        {
            codeDomProvider.GenerateCodeFromExpression(typeReferenceExpression, writer, new CodeGeneratorOptions());
            return writer.GetStringBuilder().ToString();
        }
    }
    

    Note that this includes the type namespaces, but excludes the assembly references. For the type in your question, the result looks like this:

    System.Collections.Generic.List<System.Collections.Generic.Dictionary<int, string>>
    

    It isn't clear to me whether that qualifies as "something like" List<Dictionary<int, string>>.

    0 讨论(0)
  • 2020-12-06 01:11

    Reflection - Getting the generic parameters from a System.Type instance

    You can also use reflection on generic types:

    var dict = new Dictionary<string, int>();
    
        Type type = dict.GetType();
        Console.WriteLine("Type arguments:");
        foreach (Type arg in type.GetGenericArguments())
        {
            Console.WriteLine("  {0}", arg);
        }
    

    You can then put it into some extension method for object and use it anywhere you need. I would also like to add that every recursion can be written as imperative code.

    So the whole code will look like:

     static void GetGenericParametersNames(Type type)
            {
                Queue<Type> typeQueue = new Queue<Type>();
                typeQueue.Enqueue(type);
                while (typeQueue.Any())
                {
                    var t = typeQueue.Dequeue();
                    Console.WriteLine("  {0}", arg);
    
                    foreach (Type arg in t.GetGenericArguments())
                    {
                        typeQueue.Enqueue(t);
                    }
                }
            }
    
    0 讨论(0)
  • 2020-12-06 01:17

    I used this code, when I needed a solution:

        public static string FriendlyName(this Type type)
        {
            if (type.IsGenericType)
            {
                var namePrefix = type.Name.Split(new [] {'`'}, StringSplitOptions.RemoveEmptyEntries)[0];
                var genericParameters = type.GetGenericArguments().Select(FriendlyName).ToCsv();
                return namePrefix + "<" + genericParameters + ">";
            }
    
            return type.Name;
        }
    

    and

        public static string ToCsv(this IEnumerable<object> collectionToConvert, string separator = ", ")
        {
            return String.Join(separator, collectionToConvert.Select(o => o.ToString()));
        }
    

    example usage:

        var typeDisplayText = MyDataModel.GetType().FriendlyName();
    

    ...and if you're creating auto-generated developer help pages, this can also be useful, as it includes the generic parameter names:

    public static string DefinitionTitle(this Type type)
        {
            if (type.IsGenericType)
            {
                var namePrefix = type.Name.Split(new[] { '`' }, StringSplitOptions.RemoveEmptyEntries)[0];
                var genericParameters = type.GetGenericArguments().Select(a => a.Name).ToCsv();
                return namePrefix + "<" + genericParameters + ">";
            }
    
            return type.Name;
        }
    

    example usage:

        var typeDefinitionText = typeof(Dictionary<,>).DefinitionTitle());
    
    0 讨论(0)
  • 2020-12-06 01:18

    Based on your edited question, you want something like this:

    public static string GetFriendlyName(this Type type)
    {
        if (type == typeof(int))
            return "int";
        else if (type == typeof(short))
            return "short";
        else if (type == typeof(byte))
            return "byte";
        else if (type == typeof(bool)) 
            return "bool";
        else if (type == typeof(long))
            return "long";
        else if (type == typeof(float))
            return "float";
        else if (type == typeof(double))
            return "double";
        else if (type == typeof(decimal))
            return "decimal";
        else if (type == typeof(string))
            return "string";
        else if (type.IsGenericType)
            return type.Name.Split('`')[0] + "<" + string.Join(", ", type.GetGenericArguments().Select(x => GetFriendlyName(x)).ToArray()) + ">";
        else
            return type.Name;
    }
    
    0 讨论(0)
提交回复
热议问题