【问题标题】:GetType return Int instead of System.Int32GetType 返回 Int 而不是 System.Int32
【发布时间】:2019-05-29 02:28:45
【问题描述】:

GetType().ToString() 返回对象的 FullName。我想要您通常用来实例化该对象的名称,即 int 而不是 Int32。有没有办法做到这一点?

【问题讨论】:

  • 为什么?无论如何你不能......你将不得不建立一个 Dictionary 并将它们映射到你自己。没有 reflection 方法可以获取类型 alias... 例如 int 而不是 Int32,因为 Int32 是实际的 type i> 不是int 这是别名
  • 值得注意的是,并非所有 .NET 程序集都是用 c# 编写的。如果你的不是,那么感兴趣的变量可能没有被声明为 int,这是一个 c# 关键字(例如,在 Visual Basic 中它是 Integer)。字符串 Int32 是与语言类型无关的 .NET 名称。

标签: c# .net reflection


【解决方案1】:

C# 有许多“类型”,它们实际上是 .NET CLR Types 的关键字别名。在这种情况下,intSystem.Int32 的 C# 别名,但其他 C# 类型也是如此,例如 stringSystem.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 值是这样递归处理的:

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&lt;int&gt;Dictionary&lt;int, string&gt; 或类似的事情相对容易。在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&lt;int, string&gt;)) 等等。它还处理泛型类型定义:TypeNameOrAlias(typeof(Dictionary&lt;,&gt;)) 将返回 Dictionary&lt;TKey,TValue&gt;

当您将类嵌套在泛型中时,事情就会变得困难。尝试GetTypeName(typeof(Dictionary&lt;int, string&gt;.KeyCollection)) 以获得有趣的结果。

【讨论】:

  • 感谢您的详细回复!为什么递归处理非简单类型?
  • 递归是必需的,因为在获得要别名的简单类型之前,您无法知道树的深度。示例 int?[][] 是 3 级递归以到达 int
  • 惊人的答案!非常感谢
【解决方案2】:
GetType().ToString().FromDotNetTypeToCSharpType();

使用下面的扩展方法。对于我在 c# 中使用以下 Microsoft 参考所做的一些模板,我想出了类似的东西:

https://docs.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}";

    }

【讨论】:

    猜你喜欢
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-15
    • 2015-03-03
    相关资源
    最近更新 更多