19

有没有一种简单的方法,无需编写递归方法,该方法将为Type类中的泛型类型提供“用户友好”的名称?

例如,对于下面的代码,我想要类似 'List<Dictionary<Int>>' 的东西,而不是下面代码给出的简写或全名:

var list = new List<Dictionary<int, string>>();
var type = list.GetType();

Console.WriteLine(type.Name);
Console.WriteLine(type.FullName);
4

5 回答 5

38

根据您编辑的问题,您需要这样的内容:

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;
}
于 2013-05-09T16:19:51.863 回答
15

您可以通过调用已经为您提供的递归方法来避免编写递归方法:

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

请注意,这包括类型命名空间,但不包括程序集引用。对于您问题中的类型,结果如下所示:

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

我不清楚这是否符合“类似”的条件List<Dictionary<int, string>>

于 2013-05-09T16:45:36.430 回答
6

当我需要解决方案时,我使用了这段代码:

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

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

示例用法:

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

...如果您正在创建自动生成的开发人员帮助页面,这也很有用,因为它包含通用参数名称:

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

示例用法:

    var typeDefinitionText = typeof(Dictionary<,>).DefinitionTitle());
于 2015-11-30T14:33:57.400 回答
2

从柯克的角度构建一个更完整的答案看起来像这样。修改:

  • 支持所有 C# 关键字
  • 支持自定义翻译
  • 数组
  • NullablesValueType?代替Nullable<ValueType>

这是完整的代码:

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);
    }
}
于 2017-01-31T15:51:11.510 回答
0

反射 - 从 System.Type 实例获取泛型参数

您还可以对泛型类型使用反射:

var dict = new Dictionary<string, int>();

    Type type = dict.GetType();
    Console.WriteLine("Type arguments:");
    foreach (Type arg in type.GetGenericArguments())
    {
        Console.WriteLine("  {0}", arg);
    }

然后,您可以将其放入对象的某些扩展方法中,并在需要的任何地方使用它。我还想补充一点,每个递归都可以写成命令式代码。

所以整个代码看起来像:

 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);
                }
            }
        }
于 2013-05-09T16:34:06.783 回答