GetType().ToString()返回对象的完全限定名。我想要的是通常用来实例化该对象的名称,例如int而不是Int32。有没有方法可以做到这一点?
GetType().ToString()返回对象的完全限定名。我想要的是通常用来实例化该对象的名称,例如int而不是Int32。有没有方法可以做到这一点?
C#有许多“类型”,实际上是.NET CLR Type
的关键字别名。在这种情况下,int
是C#对System.Int32
的别名,但对于其他C#类型也是如此,例如string
是对System.String
的别名。
这意味着,当您通过反射深入挖掘并查看CLR Type
对象时,您不会找到int
、string
或任何其他C#类型别名,因为.NET和CLR并不知道它们...也不应该知道它们。
如果您想从CLR类型转换为C#别名,则需要通过查找自己完成。例如:
// This is the set of types from the C# keyword list.
static Dictionary<Type, string> _typeAlias = new Dictionary<Type, string>
{
{ typeof(bool), "bool" },
{ typeof(byte), "byte" },
{ typeof(char), "char" },
{ typeof(decimal), "decimal" },
{ typeof(double), "double" },
{ typeof(float), "float" },
{ typeof(int), "int" },
{ typeof(long), "long" },
{ typeof(object), "object" },
{ typeof(sbyte), "sbyte" },
{ typeof(short), "short" },
{ typeof(string), "string" },
{ typeof(uint), "uint" },
{ typeof(ulong), "ulong" },
// Yes, this is an odd one. Technically it's a type though.
{ typeof(void), "void" }
};
static string TypeNameOrAlias(Type type)
{
// Lookup alias for type
if (_typeAlias.TryGetValue(type, out string alias))
return alias;
// Default to CLR type name
return type.Name;
}
对于简单类型,这将很好地工作。泛型、数组和Nullable
需要更多的工作。数组和Nullable
值会像这样进行递归处理:
对于简单类型,这将是有效的。对于泛型、数组和Nullable
,需要更多的工作。数组和Nullable
值将被递归处理,如下所示:
static string TypeNameOrAlias(Type type)
{
// Handle nullable value types
var nullbase = Nullable.GetUnderlyingType(type);
if (nullbase != null)
return TypeNameOrAlias(nullbase) + "?";
// Handle arrays
if (type.BaseType == typeof(System.Array))
return TypeNameOrAlias(type.GetElementType()) + "[]";
// Lookup alias for type
if (_typeAlias.TryGetValue(type, out string alias))
return alias;
// Default to CLR type name
return type.Name;
}
这将处理以下事项:
Console.WriteLine(TypeNameOrAlias(typeof(int?[][])));
如果需要泛型,涉及的过程会稍微复杂一些,但基本上是相同的。扫描泛型参数列表并递归地将类型通过该过程运行。
当在嵌套类型上运行TypeNameOrAlias
时,结果仅为特定类型的名称,而不是从声明它的类型外部使用它所需指定的完整路径:
public class Outer
{
public class Inner
{
}
}
// TypeNameOrAlias(typeof(Outer.Inner)) == "Inner"
这解决了问题:
static string GetTypeName(Type type)
{
string name = TypeNameOrAlias(type);
if (type.DeclaringType is Type dec)
{
return $"{GetTypeName(dec)}.{name}";
}
return name;
}
// GetTypeName(typeof(Outer.Inner)) == "Outer.Inner"
.NET类型系统中的泛型是很有趣的。处理像List<int>
或Dictionary<int, string>
等内容相对容易。将以下内容插入TypeNameOrAlias
的顶部:
// Handle generic types
if (type.IsGenericType)
{
string name = type.Name.Split('`').FirstOrDefault();
IEnumerable<string> parms =
type.GetGenericArguments()
.Select(a => type.IsConstructedGenericType ? TypeNameOrAlias(a) : a.Name);
return $"{name}<{string.Join(",", parms)}>";
}
现在你将获得正确的结果,例如TypeNameOrAlias(typeof(Dictionary<int, string>))
等。它还处理泛型类型定义:TypeNameOrAlias(typeof(Dictionary<,>))
将返回Dictionary<TKey,TValue>
。
当你将类嵌套在泛型中时,情况会变得更加困难。尝试使用GetTypeName(typeof(Dictionary <int,string>.KeyCollection))
获取有趣的结果。
int?[][]
需要3层递归才能到达int
。 - CoreyGetType().ToString().FromDotNetTypeToCSharpType();
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/built-in-types-table
可选地,可以通过使用简化的可空语法来传递一个布尔值,用于指示类型是否可为空。
/// <summary>Converts a .Net type name to a C# type name. It will remove the "System." namespace, if present,</summary>
public static string FromDotNetTypeToCSharpType(this string dotNetTypeName, bool isNull = false)
{
string cstype = "";
string nullable = isNull ? "?" : "";
string prefix = "System.";
string typeName = dotNetTypeName.StartsWith(prefix) ? dotNetTypeName.Remove(0, prefix.Length) : dotNetTypeName;
switch (typeName)
{
case "Boolean": cstype = "bool"; break;
case "Byte": cstype = "byte"; break;
case "SByte": cstype = "sbyte"; break;
case "Char": cstype = "char"; break;
case "Decimal": cstype = "decimal"; break;
case "Double": cstype = "double"; break;
case "Single": cstype = "float"; break;
case "Int32": cstype = "int"; break;
case "UInt32": cstype = "uint"; break;
case "Int64": cstype = "long"; break;
case "UInt64": cstype = "ulong"; break;
case "Object": cstype = "object"; break;
case "Int16": cstype = "short"; break;
case "UInt16": cstype = "ushort"; break;
case "String": cstype = "string"; break;
default: cstype = typeName; break; // do nothing
}
return $"{cstype}{nullable}";
}
这个使用 CSharpCodeProvider,处理泛型并在需要时添加命名空间。
using System;
using System.CodeDom;
using System.Collections.Generic;
using Microsoft.CSharp;
//...
private string GetFriendlyTypeName(Type type)
{
using (var p = new CSharpCodeProvider())
{
var r = new CodeTypeReference(type);
return p.GetTypeOutput(r);
}
}
恰当地给予信用。
int
而不是Int32
,因为Int32
是实际的类型,而int
是别名。 - TheGeneralint
,这是C#关键字(例如,在Visual Basic中是Integer
)。字符串Int32
是.NET中语言无关类型的名称。 - John Wu