C# 反射:如何获取 Nullable<int> 的类型?

39
我想要做的是像这样的事情:
switch( myObject.GetType().GetProperty( "id") )
{
    case ??: 
        // when Nullable<Int32>, do this
    case ??:
        // when string, do this
    case ??:
        // when Nullable<bool>, do this

在object.GetType()下,哪个路径会有数据类型的字符串名称,我可以使用case语句进行比较? 我需要知道类型,以便使用反射中的多个Convert.ToInt32(string)之一设置myObject的值。


1
这几乎肯定是在做错事情。为什么不利用多态性,而是使用一个巨大的switch语句? - Cody Gray
如果您建议创建具有不同参数类型的多个函数,我可以这样做。在这种情况下,我正在从一个对象复制一组不同类型的属性到另一个对象,其中一个始终是字符串类型。因此,我需要转换值以分配它。而且我对反射几乎没有经验。 - Zachary Scott
1
@CodyGray 他可能在做一些跟反射有关的事情,比如编写自己的ORM,或者为所有的DAO编写一个自定义序列化器,你永远不知道。 - nawfal
这就是为什么它是“几乎肯定”,而不是“确定”的原因。即使他正在做这些事情,也有理由认为这些也是错误的。关于ORM为什么是一种糟糕的数据库模式已经写了很多,我没有看到任何令人信服的理由,为什么你不能编写一个使用多态类型的序列化程序。评论的目的是让Zim博士思考这是否真的是最好的选择,这就是为什么它是评论而不是答案的原因。@nawfal - Cody Gray
@CodyGray 我理解你对于“谨慎”的看法,但是在这里使用“绝对”的词汇来表达你的观点似乎有些过于强烈了。我并不认为ORM是低效的(恰恰相反),我也能够理解自定义序列化实现需要这样做的情况。前几天,我正在编写一个同步框架,用于将来自两个不同源的数据进行同步,通过反射成员比在项目中实现接口要快得多,如果你需要快速而简单的解决方案。沿着这些思路...当约定优于配置时...等等。到处都强行使用面向对象编程并不是一个好主意。 - nawfal
5个回答

94

我一直在使用以下类型的代码来检查类型是否可为空并获取实际类型:

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
    return Nullable.GetUnderlyingType(type);
}

如果类型是可空类型,此代码将返回整数部分(基础类型)。如果你只需要将对象转换为特定类型,可以使用 System.Convert.ChangeType 方法。

3
+1值得注意的是,此方法在内部执行相同的检查,如果给定的类型不是typeof(Nullable <>),则返回null,因此之后进行空值检查而不是if语句也可以起作用。 - Connell

19
这个问题很令人困惑。 "myObject" 是可能为空的 int 对象吗?还是属性 "id" 可能是可空的 int 类型?
如果是前者,你的问题无法回答,因为它预设了一个错误的事实。没有这样的东西叫做可空 int 的装箱对象。我注意到所有提议使用 if (myobject.GetType() == typeof(int?)) 的答案都是不正确的;该条件永远不会成立。
当将可空 int 转换为对象时,它要么变成 null 引用(如果可空 int 没有值),要么变成装箱的 int。无法确定对象是否包含可空 int,因为对象从来不包含可空类型。
如果是后者,则比较属性类型和 typeof(int?)。你不能使用 switch 来比较类型;只有常量可以用于 switch cases,而类型不是常量。
尽管如此,这是一个糟糕的代码气味。你为什么要首先使用反射?

更新:在C# 7中,switch语句不再需要常量表达式 :) - Sam Rueby
提供一种类型切换机制。该功能最初是在2001年提出的,我想。所以,16年了,还不错。 - Eric Lippert
类型不是常量。我本以为类型会是常量,因为在运行时它们很难改变(除了 DLR)。这可能解释了(或者只是证明/被证明)所有的 string 类型都被更改为 ReadOnlySpan<char> =/ - StingyJack

17

更新:看起来C# 7将支持像提问者所尝试的那样在Type上进行switch。不过有一点不同,因此要注意语法陷阱。

您不需要一个字符串名称来进行比较:

if (myObject.GetType().GetProperty("id").PropertyType == typeof(Nullable<Int32>))
    // when Nullable<Int32>, do this
else if (myObject.GetType().GetProperty("id").PropertyType == typeof(string))
    // when string, do this
else if (myObject.GetType().GetProperty("id").PropertyType == typeof(Nullable<bool>))
    // when Nullable<bool>, do this

1
你确定要比较.GetType()和typeof(Nullable<Int32>),还是它是GetType()下的一个属性?假设int?和Nullable<Int32>是相同的类型,则a.GetType() != typeof(Nullable<Int32>)。 - Zachary Scott
3
就像Eric所说的那样,没有Nullable<T>的盒装版本。如果nullable不为null,则将其装箱为基础类型,否则为null(无类型)。因此,你的代码将无法工作。 - CodesInChaos
2
这个结构是存在的。但是 GetType() 永远不会返回 Nullable<T>,因为 GetType() 是在装箱值上操作的,而可空值不会作为 Nullable<T> 装箱。因此,在您的示例中,第一个和第三个 if 都永远不会被满足。 - CodesInChaos
5
他正在处理 myObject.GetType().GetProperty("ID").PropertyType。该属性的静态类型可以是 Nullable<T>,只是 GetType() 方法不能返回可空类型。 - CodesInChaos
1
修正的答案晚了几年,无法帮助提问者,但人们仍然会来这里,希望它能帮助其他人。 - M.Babcock
显示剩余4条评论

2
在.NET中,值类型的实例只是一组位,没有关联的类型信息。除了Nullable<T>之外的每个值类型,系统还会自动生成一个对应的类类型,该类型派生自System.ValueType。从值类型到自动生成的类类型存在扩展转换,而从自动生成的类类型到值类型存在缩小转换。在Nullable<T>的情况下,不存在与值类型相对应的自动生成的类类型以及与T相关联的类类型之间的转换;相反,在Nullable<T>和与T相关联的类类型之间存在双向扩展转换。
据我所知,这种奇怪的行为是为了允许将null和空的Nullable<T>进行比较并返回true。

-1

正如 @Cody Gray 所说,if 语句可能是最好的方法。

var t = myObject.GetType();

if (t == typeof(Nullable<int>))
{ }
else if (t == typeof(string))
{}
else if (t==typeof(Nullable<bool>))
{}

2
有趣的是,当 int? a = 3 时,typeof(a) 是 "System.Int32"。 - Zachary Scott
1
@Dr.Zim: 点此查看解释:https://dev59.com/W3RA5IYBdhLWcg3w6SRH - Rick Sladkey
1
@Dr.Zim typeof(a) 甚至无法编译。是 a.GetType() 返回 int - CodesInChaos
错误答案。 如果 myObject 声明为 intint?,则 myObject.GetType 总是会返回 int - nawfal

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