GetType返回Int而不是System.Int32。

5

GetType().ToString()返回对象的完全限定名。我想要的是通常用来实例化该对象的名称,例如int而不是Int32。有没有方法可以做到这一点?


为什么?无论如何你都不能...你必须建立一个字典并自己映射它们。没有反射的方法可以获取类型的别名,例如int而不是Int32,因为Int32是实际的类型,而int别名 - TheGeneral
1
值得注意的是,并非所有的.NET程序集都是用C#编写的。如果你的程序集不是用C#编写的,那么你感兴趣的变量可能没有声明为int,这是C#关键字(例如,在Visual Basic中是Integer)。字符串Int32是.NET中语言无关类型的名称。 - John Wu
3个回答

14

C#有许多“类型”,实际上是.NET CLR Type的关键字别名。在这种情况下,int是C#对System.Int32的别名,但对于其他C#类型也是如此,例如string是对System.String的别名。

这意味着,当您通过反射深入挖掘并查看CLR Type对象时,您不会找到intstring或任何其他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))获取有趣的结果。


感谢您详细的回复!为什么非简单类型要进行递归处理? - user2793618
1
递归是必需的,因为在到达要别名的简单类型之前,您无法知道树有多深。例如,int?[][]需要3层递归才能到达int - Corey
非常棒的答案!非常感谢。 - Avrohom Yisroel

3
GetType().ToString().FromDotNetTypeToCSharpType();

使用下面的扩展方法,我在C#中进行模板化时得到了类似的结果,参考了以下微软文档:

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}";

    }

3

这个使用 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);
    }
}

恰当地给予信用


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