如何获取泛型类型参数的类型名称?

175
如果我的方法签名像这样:
public string myMethod<T>( ... )

在方法内部,我如何获取作为类型参数给定的类型的名称? 我想要做一些类似于 typeof(T).FullName 的事情,但实际上并不起作用...


14
typeof(T).FullName 应该可以工作。但实际发生了什么? - Nathan Taylor
我在那个语句上遇到了编译器错误 - 但显然它们是由其他原因引起的,因为现在它正在工作。谢谢! - Tomas Aschan
6个回答

263

您的代码应该可以工作。 typeof(T).FullName 是完全有效的。 这是一个完全编译,运行的程序:

using System;

class Program 
{
    public static string MyMethod<T>()
    {
        return typeof(T).FullName;
    }

    static void Main(string[] args)
    {
        Console.WriteLine(MyMethod<int>());

        Console.ReadKey();
    }

}

运行上述代码会打印出以下内容(如预期):
System.Int32

确保使用MyMethod<int>>()进行测试并查看结果...如果您关心该场景中的基础类型,则必须考虑可空类型。 - GR7
1
你是指 "<int?>" 吗?如果是,它可以工作,但是你会得到 System.Nullable<int>(完整名称语法),这是你所期望的... - Reed Copsey
尽管我已经有了解决方案(虽然由于某些原因它没有起作用...),但你写的答案迄今为止最好,我会给你声望点数=) - Tomas Aschan
7
因为我原来认为nameof(T)typeof(T).Name会得到相同的结果,所以这篇文章对我很有帮助。结果发现nameof(T)只返回T - dahvyd

22

这个扩展方法输出非泛型类型的简单类型名称,并为泛型类型附加泛型参数列表。对于不需要担心内部泛型参数的情况,例如IDictionary<int,IDictionary<int,string>>,这种方法很有效。

using System;
using System.Linq;

namespace Extensions
{ 
    public static class TypeExtensions
    {
        /// <summary>
        /// Returns the type name. If this is a generic type, appends
        /// the list of generic type arguments between angle brackets.
        /// (Does not account for embedded / inner generic arguments.)
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>System.String.</returns>
        public static string GetFormattedName(this Type type)
        {
            if(type.IsGenericType)
            {
                string genericArguments = type.GetGenericArguments()
                                    .Select(x => x.Name)
                                    .Aggregate((x1, x2) => $"{x1}, {x2}");
                return $"{type.Name.Substring(0, type.Name.IndexOf("`"))}"
                     + $"<{genericArguments}>";
            }
            return type.Name;
        }
    }
}

6
你可以使用递归,并将 ".Select(x => x.Name)" 替换为 ".Select(x => x.GetFormattedName())"。这样所有的类型都会被格式化。 - Eliahu Aaron
2
更好的是:string.Join(",", type.GetGenericArguments().Select(GetFormattedName)) - Jeremy Lakeman

17

typeof(T).Nametypeof(T).FullName对我有效,我得到了作为参数传递的类型。


3
啊。如果你传递的类型是 Nullable,你将需要使用像 typeof(T).GetGenericArguments()[0] 这样的方式来获取基础类型。 - GR7
2
要检查类型是否可为空,您可以使用 typeof(T).IsGenericType,如果是,则使用以下内容获取名称或完整名称((Type)typeof(T).GetGenericArguments()[0]).Name - GR7

6

你的代码应该能够正常工作。你也可以获取类的名称而不包括命名空间的完整名称,例如:

using System;

namespace ConsoleApp1
{
    class Program
    {
        public static string GettingName<T>() => typeof(T).Name;

        public static string GettingFullName<T>() => typeof(T).FullName;

        static void Main(string[] args)
        {
            Console.WriteLine($"Name: {GettingName<decimal>()}");
            Console.WriteLine($"FullName: {GettingFullName<decimal>()}");
        }
    }
}

在运行上述程序后,输出结果为:
Name: Decimal
FullName: System.Decimal

这里是上述代码的 .NET Fiddle 版本,您可以自行尝试


2
假设您有一个T实例可用,它与任何其他类型没有区别。
var t = new T();

var name = t.GetType().FullName;

3
你甚至不需要一个 T 的实例... typeof(T) 可以在没有实例的情况下正常工作... 如果将子类(作为参数)传递到该方法中,你的方法将会产生不同的行为... - Reed Copsey
1
那段代码的问题在于,如果T没有无参构造函数,则它将无法工作。 - Nathan Taylor
@Nathan - 这只是一个示例,展示如何获取T的实例。假设在一个通用方法中,他将有一些T类型可用。@Reed - 当然,你是正确的,我假设这就是他想要的。 - womp
另一个问题是,如果T是抽象类或接口,则上述代码将无法工作。 如果有泛型类型约束(“where”),那么这种类型的代码可能是安全的,因为我们知道构造函数并且实际上可能有理由实例化该类型。 除此之外,实例化是浪费的。 - Andrew

1
private static string ExpandTypeName(Type t) =>
    !t.IsGenericType || t.IsGenericTypeDefinition
    ? !t.IsGenericTypeDefinition ? t.Name : t.Name.Remove(t.Name.IndexOf('`'))
    : $"{ExpandTypeName(t.GetGenericTypeDefinition())}<{string.Join(',', t.GetGenericArguments().Select(x => ExpandTypeName(x)))}>";

不错!你还可以使用.Select(ExpandTypeName) - kofifus
看起来很糟糕,但它能用! - undefined

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