获取类型名称而不包含任何泛型信息

30
如果我写下如下内容:
var type = typeof(List<string>);
Console.WriteLine(type.Name);

它将会写入:

List`1

我想让它只写入:

List

我该如何做到这一点呢? 有没有更聪明的方法可以避免使用Substring或类似的字符串操作函数?


2
从C# 6开始,您可以使用nameof(List<string>)来完成此操作。 - Nick Strupat
6个回答

47
不,将通用性数包括在名称中是有道理的——因为它是使名称唯一的一部分(当然还有程序集和命名空间)。
可以这样说:System.NullableSystem.Nullable<T> 是非常不同的类型。人们并不希望将它们混淆……所以如果你想要丢失信息,就必须付出努力。当然,这并不难做到,并且可以放入帮助方法中:
public static string GetNameWithoutGenericArity(this Type t)
{
    string name = t.Name;
    int index = name.IndexOf('`');
    return index == -1 ? name : name.Substring(0, index);
}

然后:

var type = typeof(List<string>);
Console.WriteLine(type.GetNameWithoutGenericArity());

2
一个实际的使用案例是将通用领域对象自动转换为DTO,而DTO通常不支持泛型。 - BartoszKP
以下是来自此答案的代码[在静态类和命名空间中,以便更轻松地复制和粘贴。此外,还有另一个扩展方法可以获取其命名空间的类型。](https://dev59.com/Amw15IYBdhLWcg3wzO9_#70524352) - Jesus is Lord
使用正则表达式并进行替换,这样不是更好吗?Regex.Replace(t.Name, @"\[.*\]", string.Empty); - Rodrigo Reis
1
@RodrigoReis:“更好”的方式是什么?就我个人而言,我发现正则表达式方法更难阅读 - 它需要比简单的字符串操作更长的时间来理解。当然,你可能会发现正则表达式更容易阅读 - 可读性是主观的。 - Jon Skeet
1
@RodrigoReis:你的意思很明显,你认为这样做会更好-为什么要给出一个具体的例子,然后却不起作用呢?我猜我可以写一个正则表达式来解决这个问题,但是这需要比给出的答案更长的时间,并且对于我来说不够可读。但是如果你真的只是问一个完全无辜的问题“这样做会不会更好”,那么我可以轻松回答:“不,我不认为正则表达式是这个问题的更好解决方案”。 - Jon Skeet
显示剩余4条评论

2
不,它不会,因为“generic-type-string”是类型名称的一部分。

2
如果有人感兴趣,我创建了一些扩展方法来解决这个问题,可以创建一个更易读的字符串。
它会生成类似于以下内容:
List[string]
outer.inner[other.whatever]
IEnumerable[T0]
Dictionary[string:int]

Test here

public static class TypeEx
{
    public static string GetTypeName(this Type type)
    {
        if (type == null)
            throw new ArgumentNullException(nameof(type));

        if (!type.IsGenericType)
            return type.GetNestedTypeName();

        StringBuilder stringBuilder = new StringBuilder();
        _buildClassNameRecursiv(type, stringBuilder);
        return stringBuilder.ToString();
    }

    private static void _buildClassNameRecursiv(Type type, StringBuilder classNameBuilder, int genericParameterIndex = 0)
    {
        if (type.IsGenericParameter)
            classNameBuilder.AppendFormat("T{0}", genericParameterIndex + 1);
        else if (type.IsGenericType)
        {
            classNameBuilder.Append(GetNestedTypeName(type) + "[");
            int subIndex = 0;
            foreach (Type genericTypeArgument in type.GetGenericArguments())
            {
                if (subIndex > 0)
                    classNameBuilder.Append(":");

                _buildClassNameRecursiv(genericTypeArgument, classNameBuilder, subIndex++);
            }
            classNameBuilder.Append("]");
        }
        else
            classNameBuilder.Append(type.GetNestedTypeName());
    }

    public static string GetNestedTypeName(this Type type)
    {
        if (type == null)
            throw new ArgumentNullException(nameof(type));
        if (!type.IsNested)
            return type.Name;

        StringBuilder nestedName = new StringBuilder();
        while(type != null)
        {
            if(nestedName.Length>0)
                nestedName.Insert(0,'.');

            nestedName.Insert(0, _getTypeName(type));

            type = type.DeclaringType;
        }
        return nestedName.ToString();
    }

    private static string _getTypeName(Type type)
    {
        return type.IsGenericType ? type.Name.Split('`')[0]: type.Name;
    }
}

1

我能想到的最简单的方法是在C#6中,你可以这样做:

public static void Main(string[] args)
{
    Console.WriteLine(nameof(List<int>));
    Console.WriteLine(nameof(Dictionary<int, int>));
}

这将打印:

List
Dictionary

0
static void Main(string[] args)
{

    Console.WriteLine(WhatIsMyType<IEnumerable<string>>());
    Console.WriteLine(WhatIsMyType<List<int>>());
    Console.WriteLine(WhatIsMyType<IList<int>>());
    Console.WriteLine(WhatIsMyType<List<ContentBlob>>());
    Console.WriteLine(WhatIsMyType<int[]>());
    Console.WriteLine(WhatIsMyType<ContentBlob>());
    Console.WriteLine(WhatIsMyType<Dictionary<string, Dictionary<int, int>>>());
}

public static string WhatIsMyType<T>()
{
    return typeof(T).NameWithGenerics();
}

public static string NameWithGenerics(this Type type)
{
    if (type == null)
        throw new ArgumentNullException(nameof(type));

    if (type.IsArray)
        return $"{type.GetElementType()?.Name}[]";

    if (!type.IsGenericType) 
        return type.Name;

    var name = type.GetGenericTypeDefinition().Name;
    var index = name.IndexOf('`');
    var newName = index == -1 ? name : name.Substring(0, index);
        
    var list = type.GetGenericArguments().Select(NameWithGenerics).ToList();
    return $"{newName}<{string.Join(",", list)}>";
}

示例输出:

IEnumerable<String>
List<Int32>
IList<Int32>
List<ContentBlob>
Int32[]
ContentBlob
Dictionary<String,Dictionary<Int32,Int32>>

0
这是此答案中代码的静态类和命名空间,更容易复制粘贴。
另外,还有另一个扩展方法来获取其名称空间的类型。
using System;

namespace TODO
{
    public static class TypeExtensions
    {
        /// <summary>
        /// From: https://dev59.com/Amw15IYBdhLWcg3wzO9_#6386234
        /// </summary>
        public static string GetNameWithoutGenericArity(this Type t)
        {
            string name = t.Name;
            int index = name.IndexOf('`');
            return index == -1 ? name : name.Substring(0, index);
        }
        public static string GetFullNameWithoutGenericArity(this Type t)
        {
            var result = $"{t.Namespace}.{t.GetNameWithoutGenericArity()}";
            return result;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接