这里有两个问题:1)测试一个Type是否可以为空;2) 测试一个对象是否代表一个可为空的类型。
对于问题 1(测试类型),这是我在自己的系统中使用的解决方案:TypeIsNullable-check solution
对于问题 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> 类型参数时,执行此单个方法的最后一行所需的处理器时间比允许编译器选择前面显示的第二个方法重载所需的处理器时间要多得多。因此,最佳解决方案是使用此处说明的两种方法。
警告:此方法仅在使用原始对象引用或精确副本调用时才能可靠地工作,如示例中所示。但是,如果一个可为空的对象被装箱到另一个类型(例如对象等)而不是保持其原始的 Nullable<> 形式,则此方法将无法可靠地工作。如果调用此方法的代码未使用原始的、未装箱的对象引用或精确副本,则无法使用此方法可靠地确定对象的可空性。
在大多数编码场景中,要确定可空性必须依赖于测试原始对象的类型,而不是其引用(例如,代码必须有权访问对象的原始类型才能确定可空性)。在这些更常见的情况下,IsTypeNullable(参见链接)是确定可空性的可靠方法。
PS - 关于“可空性”
我应该重复我在另一篇文章中关于可空性的声明,该声明直接适用于正确解决该主题。也就是说,我认为这里讨论的重点不应该是如何检查一个对象是否是一个泛型的 Nullable 类型,而是是否可以将 null 值赋给该类型的对象。换句话说,我认为我们应该确定一个对象类型是否可以为空,而不是它是否可以为空。区别在于语义,即确定可空性的实际原因,这通常是最重要的。
在使用对象的类型在运行时可能未知的系统中(Web 服务、远程调用、数据库、提要等),一个常见的要求是确定是否可以将空值分配给对象,或者对象是否可能包含一个空值。对不可为空的类型执行此类操作可能会产生错误,通常是异常,这在性能和编码要求方面都非常昂贵。为了采取主动避免此类问题的首选方法,有必要确定任意类型的对象是否能够包含空值;即,它是否通常是“可空的”。
在非常实际和典型的意义上,.NET 术语中的可空性并不一定意味着对象的类型是可空的形式。事实上,在很多情况下,对象都有引用类型,可以包含空值,因此都是可以为空的;这些都没有 Nullable 类型。因此,在大多数情况下,出于实际目的,应针对可空性的一般概念进行测试,而不是可空性的依赖于实现的概念。因此,我们不应仅仅关注 .NET Nullable 类型,而是将我们对其要求和行为的理解融入到关注可空性的一般实用概念的过程中。