通过反射获取类型的别名是否可行?

31

我正在编写一个简单的代码生成应用程序,用于从DB2数据库模式构建POCO。我知道这并不重要,但如果有可用的类型别名,例如,“int”而不是“Int32”,我更喜欢使用类型别名而不是实际的系统类型名称。是否有一种使用反射的方法,可以获取类型别名而不是它的实际类型?

//Get the type name
var typeName = column.DataType.Name;

//If column.DataType is, say, Int64, I would like the resulting property generated
//in the POCO to be...

public long LongColumn { get; set; }

//rather than what I get now using the System.Reflection.MemberInfo.Name property:

public Int64 LongColumn { get; set; }

@MikaelDúiBolinder 我在你提到的问题之前就问过这个问题了。 - AJ.
啊,现在我误标记了另一个。 - Mikael Dúi Bolinder
8个回答

62

不需要,只需创建一个Dictionary<Type,string>来将所有类型映射到它们的别名。这是一个固定的集合,所以很容易完成:

private static readonly Dictionary<Type, string> Aliases =
    new Dictionary<Type, string>()
{
    { typeof(byte), "byte" },
    { typeof(sbyte), "sbyte" },
    { typeof(short), "short" },
    { typeof(ushort), "ushort" },
    { typeof(int), "int" },
    { typeof(uint), "uint" },
    { typeof(long), "long" },
    { typeof(ulong), "ulong" },
    { typeof(float), "float" },
    { typeof(double), "double" },
    { typeof(decimal), "decimal" },
    { typeof(object), "object" },
    { typeof(bool), "bool" },
    { typeof(char), "char" },
    { typeof(string), "string" },
    { typeof(void), "void" }
};

1
真遗憾,这正是我想的。不过没关系,打这么点字也不算什么大事 :-) - AJ.

33

严格来说,这并不使用反射,但是您可以通过使用CodeDOM来获取类型的别名:

Type t = column.DataType;    // Int64

string typeName;
using (var provider = new CSharpCodeProvider())
{
    var typeRef = new CodeTypeReference(t);
    typeName = provider.GetTypeOutput(typeRef);
}

Console.WriteLine(typeName);    // long

(话虽如此,我认为其他建议只需使用CLR类型到C#别名的映射可能是最好的方法。)

13

如果有人需要包含nullables的字典:

private static readonly Dictionary<Type, string> Aliases = new Dictionary<Type, string>()
    {
        { typeof(byte), "byte" },
        { typeof(sbyte), "sbyte" },
        { typeof(short), "short" },
        { typeof(ushort), "ushort" },
        { typeof(int), "int" },
        { typeof(uint), "uint" },
        { typeof(long), "long" },
        { typeof(ulong), "ulong" },
        { typeof(float), "float" },
        { typeof(double), "double" },
        { typeof(decimal), "decimal" },
        { typeof(object), "object" },
        { typeof(bool), "bool" },
        { typeof(char), "char" },
        { typeof(string), "string" },
        { typeof(void), "void" },
        { typeof(Nullable<byte>), "byte?" },
        { typeof(Nullable<sbyte>), "sbyte?" },
        { typeof(Nullable<short>), "short?" },
        { typeof(Nullable<ushort>), "ushort?" },
        { typeof(Nullable<int>), "int?" },
        { typeof(Nullable<uint>), "uint?" },
        { typeof(Nullable<long>), "long?" },
        { typeof(Nullable<ulong>), "ulong?" },
        { typeof(Nullable<float>), "float?" },
        { typeof(Nullable<double>), "double?" },
        { typeof(Nullable<decimal>), "decimal?" },
        { typeof(Nullable<bool>), "bool?" },
        { typeof(Nullable<char>), "char?" }
    };

4

根据使用字典的上述两个答案,我编写了两个基本的扩展方法,可以帮助简化使用。将此类包含在您的项目中,只需按照下面所示调用类型的Alias()或AliasOrName()方法即可使用。

示例用法:

        // returns int
        string intAlias = typeof(Int32).Alias();
        // returns int
        string intAliasOrName = typeof(Int32).AliasOrName();
        // returns string.empty
        string dateTimeAlias = typeof(DateTime).Alias();
        // returns DateTime
        string dateTimeAliasOrName = typeof(DateTime).AliasOrName();

实现方式:

public static class TypeExtensions
{
    public static string Alias(this Type type)
    {
        return TypeAliases.ContainsKey(type) ?
            TypeAliases[type] : string.Empty;
    }

    public static string AliasOrName(this Type type)
    {
        return TypeAliases.ContainsKey(type) ?
            TypeAliases[type] : type.Name;
    }

    private static readonly Dictionary<Type, string> TypeAliases = new Dictionary<Type, string>
    {
        { typeof(byte), "byte" },
        { typeof(sbyte), "sbyte" },
        { typeof(short), "short" },
        { typeof(ushort), "ushort" },
        { typeof(int), "int" },
        { typeof(uint), "uint" },
        { typeof(long), "long" },
        { typeof(ulong), "ulong" },
        { typeof(float), "float" },
        { typeof(double), "double" },
        { typeof(decimal), "decimal" },
        { typeof(object), "object" },
        { typeof(bool), "bool" },
        { typeof(char), "char" },
        { typeof(string), "string" },
        { typeof(void), "void" },
        { typeof(byte?), "byte?" },
        { typeof(sbyte?), "sbyte?" },
        { typeof(short?), "short?" },
        { typeof(ushort?), "ushort?" },
        { typeof(int?), "int?" },
        { typeof(uint?), "uint?" },
        { typeof(long?), "long?" },
        { typeof(ulong?), "ulong?" },
        { typeof(float?), "float?" },
        { typeof(double?), "double?" },
        { typeof(decimal?), "decimal?" },
        { typeof(bool?), "bool?" },
        { typeof(char?), "char?" }
    };
}

4
public string GetAlias(Type t)
{
    string typeName = "";
    using (var provider = new CSharpCodeProvider())
    {
        var typeRef = new CodeTypeReference(t);
        typeName = provider.GetTypeOutput(typeRef);
    }
    return typeName;
}

2

Keep it simple:

var aliasDict = new Dictionary<Type, string>() {
    { typeof(int), "int" },
    { typeof(long), "long" },
    // etc
}

Type reflectedType;
string aliasedTypeName = aliasDict[reflectedType];

1

我认为没有。别名完全是编译时概念,特定于您使用的 .NET 语言。一旦反射并查看类型,您将看到对象的真正 .NET 类型。


1
字典的答案是灵活的,但如果我们只想要基本类型,我们可以做得更好:
public static class TypeNameExtensions
{
    private static readonly string[] TypeAliases = {
        "void",     // 0
        null,       // 1 (any other type)
        "DBNull",   // 2
        "bool",     // 3
        "char",     // 4
        "sbyte",    // 5
        "byte",     // 6
        "short",    // 7
        "ushort",   // 8
        "int",      // 9
        "uint",     // 10
        "long",     // 11
        "ulong",    // 12
        "float",    // 13
        "double",   // 14
        "decimal",  // 15
        null,       // 16 (DateTime)
        null,       // 17 (-undefined- presumably TimeSpan in some pre-1.0 C# alpha)
        "string",   // 18
    };

如果您不需要支持数组类型:

    public static bool TryGetNameAlias(this Type t, out string alias)
        => (alias = TypeAliases[(int)Type.GetTypeCode(t)]) != null && !t.IsEnum;

编辑:如果您需要支持数组类型:

    public static bool TryGetNameAlias(this Type t, out string alias)
    {
        string arrayBrackets = null;
        while (t.IsArray)
        {
            arrayBrackets += "[" + new string(',', t.GetArrayRank() - 1) + "]";
            t = t.GetElementType();
        }
        alias = TypeAliases[(int)Type.GetTypeCode(t)];
        if (alias == null || t.IsEnum)
            return false;
        alias += arrayBrackets;
        return true;
    }
}

这是基于TypeCode枚举的:
https://learn.microsoft.com/en-us/dotnet/api/system.typecode?view=netstandard-1.3
自 .Net 1.1 以来一直存在,比字典更快,因为它直接使用TypeCode作为索引跳转到数组中。唯一的缺点是,如果我们想将Object别名为object,则需要进行额外的检查。
如果你想知道为什么这个枚举存在,它被用来促进System.IConvertible的高效实现,同时暴露了GetTypeCode()方法:
https://learn.microsoft.com/en-us/dotnet/api/system.iconvertible.gettypecode?view=netstandard-1.3 因此,从本质上讲,它可以被视为原始C#开发团队中的某个人决定在v1.1时期公开的实现细节。也许是为了让其他人在需要实现与内置类型的高效操作时也可以利用它?
无论如何,它自从语言早期就一直存在,已经超过20年了! :D

个人而言,我利用TypeCode构建了一个小型库,用于通用数值范围检查和其他数字特定的元数据方法,例如使用编译时未知的通用数值类型执行算术运算,而不需要使用反射或尝试try-catch。


编辑:更新以支持数组类型(包括嵌套和多维数组)。
编辑:更新以正确处理枚举类型。


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