这里有两个问题:1)测试类型是否可为空;2)测试对象是否代表可为null的Type。
对于问题1(测试类型),这是我在自己的系统中使用的解决方案:TypeIsNullable-check解决方案
对于问题2(测试对象),上述Dean Chalk的解决方案适用于值类型,但不适用于引用类型,因为使用<T>重载始终返回false。由于引用类型本质上可以为空,因此测试引用类型应始终返回true。请参阅下面的注释[关于“可空性”],以获取这些语义的解释。因此,这是我对Dean方法的修改:
public static bool IsObjectNullable<T>(T obj)
{
// If the parameter-Type is a reference type, or if the parameter is null, then the object is always nullable
if (!typeof(T).IsValueType || obj == null)
return true;
// Since the object passed is a ValueType, and it is not null, it cannot be a nullable object
return false;
}
public static bool IsObjectNullable<T>(T? obj) where T : struct
{
// Always return true, since the object-type passed is guaranteed by the compiler to always be nullable
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); // false
isnullable = IsObjectNullable(b); // true
isnullable = IsObjectNullable(c); // true
isnullable = IsObjectNullable(d); // true
isnullable = IsObjectNullable(e); // true
isnullable = IsObjectNullable(f); // true
isnullable = IsObjectNullable(g); // true
我之所以在IsObjectNullable <T>(T t)中修改Dean的方法的原因是,对于引用类型,他的原始方法始终返回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>类型的参数时,允许编译器选择前面显示的第二种方法重载相比,执行此单个方法的最后一行要花更多的处理器时间。因此,最佳解决方案是使用此处说明的两种方法。
CAVEAT:仅当使用原始对象引用或精确副本进行调用时,此方法才能可靠地工作,如示例所示。但是,如果将可为空的对象装箱到其他类型(例如对象等),而不是保留其原始的Nullable <>形式,则此方法将无法可靠地工作。如果调用此方法的代码未使用原始的,未装箱的对象引用或精确副本,则无法使用此方法可靠地确定对象的可空性。
在大多数编码方案中,要确定可为空性,必须改为依靠测试原始对象的类型而不是其引用(例如,代码必须有权访问对象的原始Type来确定可为空性)。在这些更常见的情况下,IsTypeNullable(请参阅链接)是确定可为空性的可靠方法。
PS-关于“可空性”
我应该在另一篇文章中重复有关可空性的声明,该声明直接适用于正确解决此主题。也就是说,我相信这里的讨论重点不应是如何检查对象是否为通用Nullable类型,而是应该是否可以为该类型的对象分配null值。换句话说,我认为我们应该确定对象类型是否可为空,而不是它是否可为空。区别在于语义上,即确定可为空性的实际原因,通常这很重要。
在使用对象的类型直到运行时可能未知的系统(Web服务,远程调用,数据库,提要等)中,一个共同的要求是确定是否可以将null分配给该对象,或者该对象是否可能包含空值。对非空类型执行此类操作可能会产生错误,通常是异常,这在性能和编码要求方面都非常昂贵。为了采取主动避免此类问题的首选方法,有必要确定任意类型的对象是否能够包含null。即,它通常是否为“可为空”。
从非常实际和典型的意义上讲,.NET术语中的可为空性不一定表示对象的类型是可为空的形式。实际上,在许多情况下,对象具有引用类型,可以包含空值,因此都可以为空。这些都没有Nullable类型。因此,出于实用目的,在大多数情况下,应该针对可空性的一般概念进行测试,而不是依赖于实现的概念。因此,我们不应该只专注于.NET Nullable类型,而应该将我们对它的要求和行为的理解纳入到专注于一般的,实用的nullability概念的过程中。