提问人:AndreyAkinshin 提问时间:10/8/2009 最后编辑:AndreyAkinshin 更新时间:11/17/2023 访问量:5969
使用 C 上的 Reflection 以良好的格式获取 GenericType-Name#
Get GenericType-Name in good format using Reflection on C#
问:
我需要在代码中以声明的形式获取泛型类型的名称。
例如: 对于 List<Int32>我想获取字符串“List<Int32>”。 在这种情况下,标准属性 Type.Name 返回“List'1”。
编辑:示例已修复
答:
嗯,这是因为 .NET 中的类型名称实际上是 List'1。“'1”是泛型的所谓arity,它告诉你有多少个类型参数。
它是必要的,以便您可以创建多个具有相同“名称”但不同数量的泛型类型参数的泛型类型。
例如,有 1 种以上的类型“称为”System.Action。它们的真实名称是 System.Action'1、System.Action'2、System.Action'3 等。
所以,如果你知道你的类型是泛型的,你可以假设名称的末尾有这个'XX,所以你可以把这部分剪掉,例如,像这样:
string strTypeName = typeof(List<>).Name.Substring(0, typeof(List<>).Name.LastIndexOf("`"));
PS:请将“替换为”。
评论
好的,我做了很多研究,发现typeof(List)有“GetGenericArguments”,可以让你得到子名。所以我会这样做(对于 1 个泛型类型,如果它是多重类型,它将需要一个循环或其他东西。如果需要,我可以为此发布一个功能。
这是一个使用多个泛型参数的函数,处理“嵌套”泛型类型。再次编辑以使其使用 Aggregate 函数:
static string GetFullName(Type t)
{
if (!t.IsGenericType)
return t.Name;
StringBuilder sb=new StringBuilder();
sb.Append(t.Name.Substring(0, t.Name.LastIndexOf("`")));
sb.Append(t.GetGenericArguments().Aggregate("<",
delegate(string aggregate,Type type)
{
return aggregate + (aggregate == "<" ? "" : ",") + GetFullName(type);
}
));
sb.Append(">");
return sb.ToString();
}
评论
这并不难。;-)
好吧,我会咬...g 下面的那个是隐蔽的,并显示没有命名空间的原始类型(就像 OP 写的那样):
static string PrettyPrintGenericTypeName(Type typeRef)
{
var rootType = typeRef.IsGenericType
? typeRef.GetGenericTypeDefinition()
: typeRef;
var cleanedName = rootType.IsPrimitive
? rootType.Name
: rootType.ToString();
if (!typeRef.IsGenericType)
return cleanedName;
else
return cleanedName.Substring(0,
cleanedName.LastIndexOf('`'))
+ typeRef.GetGenericArguments()
.Aggregate("<",
(r, i) =>
r
+ (r != "<" ? ", " : null)
+ PrettyPrintGenericTypeName(i))
+ ">";
}
生成的 cleanedName 如下所示:System.Collections.Generic.Dictionary<System.Collections.Generic.List<Int32>, ConsoleApplication2.Program+SomeType>
使用内置函数和 Linq,可以编写
static string PrettyTypeName(Type t)
{
if (t.IsArray)
{
return PrettyTypeName(t.GetElementType()) + "[]";
}
if (t.IsGenericType)
{
return string.Format(
"{0}<{1}>",
t.Name.Substring(0, t.Name.LastIndexOf("`", StringComparison.InvariantCulture)),
string.Join(", ", t.GetGenericArguments().Select(PrettyTypeName)));
}
return t.Name;
}
注意:在 C# 4.0 之前的版本中,需要显式:string.Join
.ToArray()
string.Join(", ", t.GetGenericArguments().Select(PrettyTypeName).ToArray()));
评论
t.GetGenericArguments().Select(PrettyTypeName)
.ToArray()
PrettyTypeName(typeof(ICollection<Tuple<string, int, Tuple<ICollection<string>,IList>[]>[]>))
if (t.IsArray) return PrettyTypeName(t.GetElementType()) + "[]";
另一个例子是我在这里绊倒之前自己写的。
private string PrettyPrintGenericTypeName(Type p)
{
if (p.IsGenericType) {
var simpleName = p.Name.Substring(0, p.Name.IndexOf('`'));
var genericTypeParams = p.GenericTypeArguments.Select(PrettyPrintGenericTypeName).ToList();
return string.Format("{0}<{1}>", simpleName, string.Join(", ", genericTypeParams));
} else {
return p.Name;
}
}
老问题,但我今天只需要这个。因此,我编写了一个扩展方法,它可以输出美观的 C# 格式的泛型名称,该名称可以处理多级嵌套泛型类型。
using System;
using System.Text;
public static class TypeExtensions
{
public static string GetNiceName(this Type type, bool useFullName = false)
{
if (!type.IsGenericType) {
return type.Name;
}
var typeNameBuilder = new StringBuilder();
GetNiceGenericName(typeNameBuilder, type, useFullName);
return typeNameBuilder.ToString();
}
static void GetNiceGenericName(StringBuilder sb, Type type, bool useFullName)
{
if (!type.IsGenericType) {
sb.Append(useFullName ? type.FullName : type.Name);
return;
}
var typeDef = type.GetGenericTypeDefinition();
var typeName = useFullName ? typeDef.FullName : typeDef.Name;
sb.Append(typeName);
sb.Length -= typeName.Length - typeName.LastIndexOf('`');
sb.Append('<');
foreach (var typeArgument in type.GenericTypeArguments) {
GetNiceGenericName(sb, typeArgument, useFullName);
sb.Append(", ");
}
sb.Length -= 2;
sb.Append('>');
}
}
如果不删除命名空间名称,只需说:
Regex.Replace(""+@type, @"`\d+\[", "<").Replace("]", ">");
如果你这样做了,请说:
Regex.Replace(Regex.Replace(""+@type, @"`\d+\[", "<").Replace("]", ">"), @"\w+\.", "")
几天前我遇到了同样的问题,我已经编写了自己的扩展方法。
此代码适用于普通类型和“可为 null 的引用类型”。
我不是 100% 舒尔,它在所有情况下都能正常工作,但像这样的类型工作正常:
public IDictionary<MyClass?[][], Dictionary<object?,int?[,]?>>? Dummy { get; set; }
这几乎完美地满足了我的需求:
public static class ReflectionExtensions
{
public static string ToReadableTypeString(this PropertyInfo property, bool useFullnames = false, bool useInternalTypeNames= true)
{
return ToReadableTypeStringInternal(property.PropertyType, new NullabilityInfoContext().Create(property), useFullnames, useInternalTypeNames);
}
public static string ToReadableTypeString(this Type type, NullabilityInfo? nullabilityInfo = null, bool useFullnames = false, bool useInternalTypeNames= true)
{
return ToReadableTypeStringInternal(type, nullabilityInfo, useFullnames, useInternalTypeNames);
}
private static string ToReadableTypeStringInternal(Type type,NullabilityInfo? nullabilityInfo, bool useFullnames, bool useInternalTypeNames)
{
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
string suffix;
var underlyingType = Nullable.GetUnderlyingType(type);
if (underlyingType != null)
{
type = underlyingType;
suffix = "?";
}
else
{
suffix = nullabilityInfo?.ReadState == NullabilityState.Nullable ? "?" : "";
}
if (useInternalTypeNames)
{
if (type == typeof(string)) { return "string"+suffix; }
if (type == typeof(sbyte)) { return "sbyte"+suffix; }
if (type == typeof(byte)) { return "byte"+suffix; }
if (type == typeof(short)) { return "short"+suffix; }
if (type == typeof(ushort)) { return "ushort"+suffix; }
if (type == typeof(int )) { return "int"+suffix; }
if (type == typeof(uint)) { return "uint"+suffix; }
if (type == typeof(long)) { return "long"+suffix; }
if (type == typeof(ulong )) { return "ulong"+suffix; }
if (type == typeof(nint )) { return "nint"+suffix; }
if (type == typeof(nuint)) { return "nuint"+suffix; }
if (type == typeof(float)) { return "float"+suffix; }
if (type == typeof(double)) { return "double"+suffix; }
if (type == typeof(decimal)) { return "decimal"+suffix; }
if (type == typeof(bool)) { return "bool"+suffix; }
if (type == typeof(char)) { return "char"+suffix; }
if (type == typeof(object)) { return "object"+suffix; }
}
if (type.IsArray)
{
var sb = new StringBuilder(ToReadableTypeStringInternal(type.GetElementType()!, nullabilityInfo?.ElementType, useFullnames, useInternalTypeNames));
if (type.IsVariableBoundArray)
{
sb.Append('[');
sb.Append(new string(',',type.GetArrayRank()-1));
sb.Append(']');
}
else
{
for (var i = 0; i < type.GetArrayRank(); i++)
{
sb.Append("[]");
}
}
return sb+suffix;
}
if (type.IsGenericType)
{
return (useFullnames?(type.FullName??type.Name):type.Name).Split("`").First() + "<" + string.Join(",", type.GenericTypeArguments.Select((type1,index) => ToReadableTypeStringInternal(type1,nullabilityInfo?.GenericTypeArguments[index], useFullnames, useInternalTypeNames))) + ">";
}
return (useFullnames?(type.FullName??type.Name):type.Name)+suffix;
}
}
评论