这里涉及两个问题:1) 测试Type类型是否可空;2)测试对象是否表示可为空的Type。
对于第一个问题(测试Type),我在自己的系统中使用了以下解决方案:
TypeIsNullable-check solution。
对于第二个问题(测试对象),Dean Chalk提供的解决方案适用于值类型,但并不适用于引用类型,因为使用重载总是返回false。由于引用类型本质上是可为空的,因此测试引用类型应始终返回true。有关这些语义的说明,请参见下面的“nullability”说明。因此,这是我的修改意见:
public static bool IsObjectNullable<T>(T obj)
{
if (!typeof(T).IsValueType || obj == null)
return true;
return false;
}
public static bool IsObjectNullable<T>(T? obj) where T : struct
{
return true;
}
以下是关于上述解决方案的客户端测试代码的修改:
这里是我对客户端测试代码的修改:
int a = 123;
int? b = null;
object c = new object();
object d = null;
int? e = 456;
var f = (int?)789;
string g = "something";
bool isnullable = IsObjectNullable(a);
isnullable = IsObjectNullable(b);
isnullable = IsObjectNullable(c);
isnullable = IsObjectNullable(d);
isnullable = IsObjectNullable(e);
isnullable = IsObjectNullable(f);
isnullable = IsObjectNullable(g);
我修改了Dean在IsObjectNullable<T>(T t)中的方法,因为他原来的方法对于引用类型总是返回false。由于像IsObjectNullable这样的方法应该能够处理引用类型的值,并且所有引用类型本质上都是可空的,因此如果传递任何一个引用类型或null,则该方法应始终返回true。
可以用以下单个方法替换上述两个方法并实现相同的输出:
public static bool IsObjectNullable<T>(T obj)
{
Type argType = typeof(T);
if (!argType.IsValueType || obj == null)
return true;
return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>);
}
然而,使用这种最后一种单一方法的问题在于,当使用Nullable<T>参数时性能会受到影响。与允许编译器选择先前显示的第二种方法重载相比,在使用IsObjectNullable调用Nullable<T>类型参数时,执行此单个方法的最后一行需要更多的处理器时间。因此,最佳解决方案是使用此处所示的两种方法。
注意:只有在使用原始对象引用或精确副本的情况下,该方法才可靠地工作,如示例所示。然而,如果将可空对象装箱到另一个类型(如对象等)中,而不是保持其原始Nullable<>形式,则该方法将无法可靠地工作。如果调用此方法的代码未使用原始的、未装箱的对象引用或精确副本,则无法可靠地使用此方法确定对象的可空性。
在大多数编码场景中,要确定可空性,必须依赖于测试原始对象的类型,而不是其引用(例如,代码必须访问对象的原始类型以确定可空性)。在这些更常见的情况下,IsTypeNullable(请参见链接)是一种可靠的确定可空性的方法。
附言 - 关于“可空性”
我应该重申一下我在另一篇帖子中关于可空性的声明,它直接适用于正确处理此主题。也就是说,我认为这里的讨论重点不应该是如何检查对象是否为通用Nullable类型,而应该是是否可以将null值赋给其类型的对象。换句话说,我认为我们应该确定一个对象类型是否可空,而不是它是否为Nullable。区别在于语义,即确定可空性的实际原因,这通常是最重要的。
在使用可能在运行时之前未知的类型的对象的系统中(Web服务、远程调用、数据库、提要等),一个常见的要求是确定是否可以将null分配给该对象,或者该对象是否可能包含null。在非可空类型上执行此类操作通常会产生错误,通常是异常,这些错误在性能和编码要求方面非常昂贵。为了采取高度优先的方法,以预防此类问题,有必要确定任意类型的对象是否能够包含null,即是否一般“可空”。
在.NET术语中,从非常实际和典型的意义上来看,可空性根本不一定意味着对象的类型是Nullable形式的。事实上,在许多情况下,对象具有引用类型,可以包含null值,因此都是可空的;其中没有一个具有Nullable类型。因此,在大多数情况下,应该针对一般概念的可空性进行测试,而不是针对实现相关的Nullable概念。因此,我们不应该只关注.NET Nullable类型,而是在关注一般实际可空性概念的过程中融入我们对其要求和行为的理解。