C#:“漂亮”的类型名称function?
在generics类型的情况下,System.Type类的名称属性返回奇怪的结果。 有没有办法以更接近我指定方式的格式获取类型名称? 示例: typeof(List).OriginalName == "List"
“漂亮”名称的问题是它们根据您使用的语言而有所不同。 如果OriginalName
返回C#语法,想象一下VB.NET开发人员的惊喜。
但是,自己做这件事非常容易:
private static string PrettyName(Type type) { if (type.GetGenericArguments().Length == 0) { return type.Name; } var genericArguments = type.GetGenericArguments(); var typeDefeninition = type.Name; var unmangledName = typeDefeninition.Substring(0, typeDefeninition.IndexOf("`")); return unmangledName + "<" + String.Join(",", genericArguments.Select(PrettyName)) + ">"; }
这将以递归方式解析非托管名称,这样如果你有类似Dictionary
东西,它仍然可以工作。
我使用CodeDomProvider
转换为c#:
public static string GetOriginalName(this Type type) { string TypeName = type.FullName.Replace(type.Namespace + ".", "");//Removing the namespace var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); //You can also use "VisualBasic" var reference = new System.CodeDom.CodeTypeReference(TypeName); return provider.GetTypeOutput(reference); }
你必须自己写。 请记住, Type.Name
等正在调用CLR中的方法,并且可以从多种语言调用。 这就是为什么他们不会回来看起来像C#或VB或调用者编码的语言,而是看起来像CLR表示。
进一步注意string
和CLR类型的别名,如System.String
。 同样,这在您看到的格式中起作用。
使用reflection并不难,但我质疑它的价值。
像Harold Hoyer的回答一样,但包括nullables和一些内置类型:
/// /// Get full type name with full namespace names /// /// /// The type to get the C# name for (may be a generic type or a nullable type). /// /// /// Full type name, fully qualified namespaces /// public static string CSharpName(this Type type) { Type nullableType = Nullable.GetUnderlyingType(type); string nullableText; if (nullableType != null) { type = nullableType; nullableText = "?"; } else { nullableText = string.Empty; } if (type.IsGenericType) { return string.Format( "{0}<{1}>{2}", type.Name.Substring(0, type.Name.IndexOf('`')), string.Join(", ", type.GetGenericArguments().Select(ga => ga.CSharpName())), nullableText); } switch (type.Name) { case "String": return "string"; case "Int32": return "int" + nullableText; case "Decimal": return "decimal" + nullableText; case "Object": return "object" + nullableText; case "Void": return "void" + nullableText; default: return (string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName) + nullableText; } }
这是我的实施。 它是为描述方法而创建的,因此它处理ref
和out
关键字。
private static Dictionary shorthandMap = new Dictionary { { typeof(Boolean), "bool" }, { typeof(Byte), "byte" }, { typeof(Char), "char" }, { typeof(Decimal), "decimal" }, { typeof(Double), "double" }, { typeof(Single), "float" }, { typeof(Int32), "int" }, { typeof(Int64), "long" }, { typeof(SByte), "sbyte" }, { typeof(Int16), "short" }, { typeof(String), "string" }, { typeof(UInt32), "uint" }, { typeof(UInt64), "ulong" }, { typeof(UInt16), "ushort" }, }; private static string CSharpTypeName(Type type, bool isOut = false) { if (type.IsByRef) { return String.Format("{0} {1}", isOut ? "out" : "ref", CSharpTypeName(type.GetElementType())); } if (type.IsGenericType) { if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) { return String.Format("{0}?", CSharpTypeName(Nullable.GetUnderlyingType(type))); } else { return String.Format("{0}<{1}>", type.Name.Split('`')[0], String.Join(", ", type.GenericTypeArguments.Select(a => CSharpTypeName(a)).ToArray())); } } if (type.IsArray) { return String.Format("{0}[]", CSharpTypeName(type.GetElementType())); } return shorthandMap.ContainsKey(type) ? shorthandMap[type] : type.Name; }
调用代码如下所示:
string line = String.Format("{0}.{1}({2})", method.DeclaringType.Name, method.Name, String.Join(", ", method.GetParameters().Select(p => CSharpTypeName(p.ParameterType, p.IsOut) + " " + p.Name).ToArray()));
其中method
是MethodInfo
实例。
一个注意事项:我没有必要描述任何多维数组类型,所以我没有为此实现描述,但通过调用type.GetArrayRank()
来添加它会相当容易。
在你的例子中,你可以期待类型,所以你可以尝试
public class test where T : class { public List tt { get; set; } } /////////////////////////// test> tt = new test>(); if(tt.GetType().FullName.Contains(TypeOf(List ).FullName)) { //do something } else { //do something else }
我知道你想要比较类型。
最好的方法是……
myVar is List
或
myVar.GetType() == myOtherVar.GetType()
如果你不需要这个……请忽略我的答案。
我明白了,我必须自己写这个。 这是我的解决方案(它实际上比要求的要多一些)。 它可能是有用的。
using System.Reflection; using HWClassLibrary.Debug; using System.Collections.Generic; using System.Linq; using System; namespace HWClassLibrary.Helper { public static class TypeNameExtender { private static IEnumerable _referencedTypesCache; public static void OnModuleLoaded() { _referencedTypesCache = null; } public static string PrettyName(this Type type) { if(type == typeof(int)) return "int"; if(type == typeof(string)) return "string"; var result = PrettyTypeName(type); if(type.IsGenericType) result = result + PrettyNameForGeneric(type.GetGenericArguments()); return result; } private static string PrettyTypeName(Type type) { var result = type.Name; if(type.IsGenericType) result = result.Remove(result.IndexOf('`')); if (type.IsNested && !type.IsGenericParameter) return type.DeclaringType.PrettyName() + "." + result; if(type.Namespace == null) return result; var conflictingTypes = ReferencedTypes .Where(definedType => definedType.Name == type.Name && definedType.Namespace != type.Namespace) .ToArray(); var namespaceParts = type.Namespace.Split('.').Reverse().ToArray(); var namespacePart = ""; for(var i = 0; i < namespaceParts.Length && conflictingTypes.Length > 0; i++) { namespacePart = namespaceParts[i] + "." + namespacePart; conflictingTypes = conflictingTypes .Where(conflictingType => (conflictingType.Namespace + ".").EndsWith(namespacePart)) .ToArray(); } return namespacePart + result; } private static IEnumerable ReferencedTypes { get { if(_referencedTypesCache == null) _referencedTypesCache = Assembly.GetEntryAssembly().GetReferencedTypes(); return _referencedTypesCache; } } private static string PrettyNameForGeneric(Type[] types) { var result = ""; var delim = "<"; foreach(var t in types) { result += delim; delim = ","; result += t.PrettyName(); } return result + ">"; } } }
第一:向Navid致敬,以避免车轮重新发明。 如果可以,我会赞成。
如果有人走这条路(至少对于VS10 / .Net 4),这里有几点需要补充:
*尝试使用CodeTypeReference的一个变体和Type参数。 这避免了使用字符串类型名称(例如尾随&)的许多陷阱,并且意味着你得到了bool
而不是System.Boolean
等。你确实得到了很多这样的类型的完整命名空间但是你总是可以摆脱它后面的名称空间部分。
*简单的Nullables倾向于以System.Nullable
而不是int?
forms返回int?
– 您可以在答案上使用正则表达式转换为更好的语法,例如: const string NullablePattern = @"System.Nullable<(?
const string NullablePattern = @"System.Nullable<(?
*类似于out T?
的方法参数的类型out T?
给出一个非常不透明的字符串。 如果有人有一种处理这类事情的优雅方式,我很乐意了解它。
希望Roslyn能让这一切变得非常简单。
利用CodeDomProvider
最小工作解决方案是首先控制CodeTypeReference
实例的构建方式。 有一些特殊情况只适用于generics类型和多列数组,所以我们只需要关心它们:
static CodeTypeReference CreateTypeReference(Type type) { var typeName = (type.IsPrimitive || type == typeof(string)) ? type.FullName : type.Name; var reference = new CodeTypeReference(typeName); if (type.IsArray) { reference.ArrayElementType = CreateTypeReference(type.GetElementType()); reference.ArrayRank = type.GetArrayRank(); } if (type.IsGenericType) { foreach (var argument in type.GetGenericArguments()) { reference.TypeArguments.Add(CreateTypeReference(argument)); } } return reference; }
使用这个修改过的工厂方法,然后可以使用适当的代码提供程序来获得漂亮的输入,如下所示:
using (var provider = new CSharpCodeProvider()) { var reference = CreateTypeReference(typeof(IObservable>>[,])); var output = provider.GetTypeOutput(reference); Console.WriteLine(output); }
上面的代码返回IObservable
。 唯一不能很好处理的特殊情况是Nullable
类型,但这实际上更多是CodeDomProvider
的错误。