我如何检查一个给定的对象是否为空,换句话说,如何实现以下方法…

bool IsNullableValueType(object o)
{
    ...
}

我正在寻找可空值类型。我没有想到引用类型。

//Note: This is just a sample. The code has been simplified 
//to fit in a post.

public class BoolContainer
{
    bool? myBool = true;
}

var bc = new BoolContainer();

const BindingFlags bindingFlags = BindingFlags.Public
                        | BindingFlags.NonPublic
                        | BindingFlags.Instance
                        ;


object obj;
object o = (object)bc;

foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
    obj = (object)fieldInfo.GetValue(o);
}

obj现在指向bool类型(System.Boolean)的值为true的对象。我真正想要的是一个Nullable<bool>类型的对象

所以现在我决定检查o是否为空,并在obj周围创建一个可空的包装器。


当前回答

你可以用:

return !(o is ValueType);

... 但是对象本身不是可空的,而是类型。你打算怎么用这个?

其他回答

这对我来说很有效,看起来很简单:

static bool IsNullable<T>(T obj)
{
    return default(T) == null;
}

对于值类型:

static bool IsNullableValueType<T>(T obj)
{
    return default(T) == null && typeof(T).BaseType != null && "ValueType".Equals(typeof(T).BaseType.Name);
}

有一个简单的方法:

    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType) return Activator.CreateInstance(type) == null;

        return true;
    }

这些是我的单元测试,都通过了

    IsNullable_String_ShouldReturn_True
    IsNullable_Boolean_ShouldReturn_False
    IsNullable_Enum_ShouldReturn_Fasle
    IsNullable_Nullable_ShouldReturn_True
    IsNullable_Class_ShouldReturn_True
    IsNullable_Decimal_ShouldReturn_False
    IsNullable_Byte_ShouldReturn_False
    IsNullable_KeyValuePair_ShouldReturn_False

实际的单元测试

    [TestMethod]
    public void IsNullable_String_ShouldReturn_True()
    {
        var typ = typeof(string);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Boolean_ShouldReturn_False()
    {
        var typ = typeof(bool);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Enum_ShouldReturn_Fasle()
    {
        var typ = typeof(System.GenericUriParserOptions);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Nullable_ShouldReturn_True()
    {
        var typ = typeof(Nullable<bool>);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Class_ShouldReturn_True()
    {
        var typ = typeof(TestPerson);
        var result = typ.IsNullable();
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void IsNullable_Decimal_ShouldReturn_False()
    {
        var typ = typeof(decimal);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_Byte_ShouldReturn_False()
    {
        var typ = typeof(byte);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

    [TestMethod]
    public void IsNullable_KeyValuePair_ShouldReturn_False()
    {
        var typ = typeof(KeyValuePair<string, string>);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }

最简单的方法是:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}

“如何检查一个类型是否为空”的问题实际上是“如何检查一个类型是否为nullable <>?”,可以概括为“如何检查一个类型是否为某个泛型类型的构造类型?”,因此它不仅回答了“是否为nullable <int> a nullable <>?”,而且还回答了“是否为List<int> a List<>?”

大多数提供的解决方案使用Nullable. getunderlyingtype()方法,该方法显然只适用于Nullable<>的情况。我没有看到适用于任何泛型类型的一般反射解决方案,所以我决定在这里添加它以供后人使用,尽管这个问题很久以前就已经有了答案。

要使用反射检查类型是否为Nullable<>的某种形式,首先必须将构造的泛型类型(例如Nullable<int>)转换为泛型类型定义Nullable<>。您可以通过使用Type类的GetGenericTypeDefinition()方法来做到这一点。然后你可以将结果类型与Nullable<>进行比较:

Type typeToTest = typeof(Nullable<int>);
bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
// isNullable == true

同样可以应用于任何泛型类型:

Type typeToTest = typeof(List<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true

一些类型可能看起来相同,但不同数量的类型参数意味着它是一个完全不同的类型。

Type typeToTest = typeof(Action<DateTime, float>);
bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>);
bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>);
bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>);
// isAction1 == false
// isAction2 == true
// isAction3 == false

由于Type对象对每个类型实例化一次,因此可以检查它们之间的引用是否相等。所以如果你想检查两个对象是否具有相同的泛型类型定义,你可以这样写:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

如果你想检查一个对象是否为空,而不是一个类型,那么你可以使用上面的技术和Marc Gravell的解决方案来创建一个相当简单的方法:

static bool IsNullable<T>(T obj)
{
    if (!typeof(T).IsGenericType)
        return false;

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}

这里有两个问题: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.