这里有两个问题:1)测试一个类型是否为空;以及2)测试一个对象是否代表一个可空的类型。
对于问题1(测试类型),这里是我在自己的系统中使用的解决方案:typeisnulable -检查解决方案
对于问题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这样的方法应该能够处理引用类型的值,而且所有的引用类型本质上都是可空的,那么如果传递的是引用类型或空值,该方法应该总是返回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>类型参数时,执行这个方法的最后一行所花费的处理器时间要比允许编译器选择前面所示的第二个方法重载所花费的处理器时间多得多。因此,最佳解决方案是使用这里所示的两种方法。
注意:此方法只有在使用原始对象引用或精确副本调用时才能可靠地工作,如示例所示。但是,如果一个可空对象被装箱为另一个类型(如object等),而不是保持其原始的nullable <>形式,则此方法将不可靠地工作。如果调用此方法的代码没有使用原始的、未装箱的对象引用或精确的副本,则不能使用此方法可靠地确定对象的可空性。
在大多数编码场景中,要确定可空性,必须依赖于测试原始对象的Type,而不是它的引用(例如,代码必须访问对象的原始Type才能确定可空性)。在这些更常见的情况下,IsTypeNullable(见链接)是确定可空性的可靠方法。
附注:关于“可空性”
I should repeat a statement about nullability I made in a separate post, which applies directly to properly addressing this topic. That is, I believe the focus of the discussion here should not be how to check to see if an object is a generic Nullable type, but rather whether one can assign a value of null to an object of its type. In other words, I think we should be determining whether an object type is nullable, not whether it is Nullable. The difference is in semantics, namely the practical reasons for determining nullability, which is usually all that matters.
In a system using objects with types possibly unknown until run-time (web services, remote calls, databases, feeds, etc.), a common requirement is to determine whether a null can be assigned to the object, or whether the object might contain a null. Performing such operations on non-nullable types will likely produce errors, usually exceptions, which are very expensive both in terms of performance and coding requirements. To take the highly-preferred approach of proactively avoiding such problems, it is necessary to determine whether an object of an arbitrary Type is capable of containing a null; i.e., whether it is generally 'nullable'.
In a very practical and typical sense, nullability in .NET terms does not at all necessarily imply that an object's Type is a form of Nullable. In many cases in fact, objects have reference types, can contain a null value, and thus are all nullable; none of these have a Nullable type. Therefore, for practical purposes in most scenarios, testing should be done for the general concept of nullability, vs. the implementation-dependent concept of Nullable. So we should not be hung up by focusing solely on the .NET Nullable type but rather incorporate our understanding of its requirements and behavior in the process of focusing on the general, practical concept of nullability.