代码之家  ›  专栏  ›  技术社区  ›  Sandeep Datta

如何检查对象是否可以为空?

  •  180
  • Sandeep Datta  · 技术社区  · 16 年前

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

    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周围创建一个可以为空的包装器。

    14 回复  |  直到 6 年前
        1
  •  245
  •   Abel    13 年前

    有两种类型的可以为空- Nullable<T> 和引用类型。

    乔恩已经纠正了我,如果装箱很难找到类型,但是你可以用仿制药: -下面怎么样?这实际上是测试类型 T ,但是使用 obj 纯粹用于泛型类型推断的参数(为了便于调用)-如果没有 OBJ 但是,帕拉姆。

    static bool IsNullable<T>(T obj)
    {
        if (obj == null) return true; // obvious
        Type type = typeof(T);
        if (!type.IsValueType) return true; // ref-type
        if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
        return false; // value-type
    }
    

    但是,如果已经将值装箱到对象变量中,那么这就不能很好地工作。

        2
  •  46
  •   Dean Chalk    10 年前

    使用方法重载有一个非常简单的解决方案

    http://deanchalk.com/is-it-nullable/

    摘录:

    public static class ValueTypeHelper
    {
        public static bool IsNullable<T>(T t) { return false; }
        public static bool IsNullable<T>(T? t) where T : struct { return true; }
    }
    

    然后

    static void Main(string[] args)
    {
        int a = 123;
        int? b = null;
        object c = new object();
        object d = null;
        int? e = 456;
        var f = (int?)789;
        bool result1 = ValueTypeHelper.IsNullable(a); // false
        bool result2 = ValueTypeHelper.IsNullable(b); // true
        bool result3 = ValueTypeHelper.IsNullable(c); // false
        bool result4 = ValueTypeHelper.IsNullable(d); // false
        bool result5 = ValueTypeHelper.IsNullable(e); // true
        bool result6 = ValueTypeHelper.IsNullable(f); // true
    
        3
  •  30
  •   Allon Guralnek    14 年前

    “如何检查类型是否可以为空?”的问题。实际上是“如何检查类型 Nullable<> “,它可以概括为“如何检查类型是否是某个泛型类型的构造类型?”这样它不仅回答了问题“是 Nullable<int> 可以为空<gt; “,而且“是” List<int> List<> “.

    大多数提供的解决方案都使用 Nullable.GetUnderlyingType() 方法,这显然只适用于 可以为空<gt; . 我没有看到通用的反射解决方案可以与任何通用类型一起使用,所以我决定在这里为后代添加它,尽管这个问题早就被回答了。

    检查类型是否是 可以为空<gt; 例如,使用反射,首先必须转换构造的泛型类型 可以为空<int> ,进入泛型类型定义, 可以为空<gt; . 你可以通过使用 GetGenericTypeDefinition() 方法 Type 班级。然后可以将结果类型与 可以为空<gt; :

    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
    

    自从 类型 对象每类型实例化一次,您可以检查它们之间的引用是否相等。因此,如果要检查两个对象是否属于相同的泛型类型定义,可以编写:

    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<>);
    }
    
        4
  •  20
  •   Jon Skeet    16 年前

    好吧,你可以用:

    return !(o is ValueType);
    

    …但对象本身不可以为空或其他- 类型 是。你打算怎么用这个?

        5
  •  18
  •   Erik    8 年前

    这对我很有用,而且看起来很简单:

    static bool IsNullable<T>(T obj)
    {
        return default(T) == null;
    }
    
        6
  •  11
  •   CARLOS LOTH    13 年前

    最简单的方法是:

    public bool IsNullable(object obj)
    {
        Type t = obj.GetType();
        return t.IsGenericType 
            && t.GetGenericTypeDefinition() == typeof(Nullable<>);
    }
    
        7
  •  9
  •   Community CDub    7 年前

    这里有两个问题:1)测试一个类型是否可以为空;2)测试一个对象是否表示可以为空的类型。

    对于问题1(测试类型),我在自己的系统中使用了以下解决方案: TypeIsNullable-check solution

    对于问题2(测试对象),Dean Chalk的上述解决方案适用于值类型,但不适用于引用类型,因为使用<t>重载总是返回false。由于引用类型本身可以为空,因此测试引用类型时应始终返回true。有关这些语义的解释,请参见下面的注释[关于“空性”]。因此,以下是我对迪恩方法的修改:

        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)中修改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<>);
        }
    

    但是,最后一个单一方法方法的问题是,当使用可为空的<t>参数时,性能会受到影响。执行此单个方法的最后一行所需的处理器时间比允许编译器选择前面在IsObjectNullable调用中使用Nullable<t>类型参数时显示的第二个方法重载所需的处理器时间要长得多。因此,最佳解决方案是采用本文所述的两种方法。

    警告:只有在使用原始对象引用或精确副本调用时,此方法才可靠工作,如示例中所示。但是,如果一个可以为空的对象被装箱到另一个类型(如对象等),而不是保留在其原始的可以为空的形式中,则此方法将无法可靠地工作。如果调用此方法的代码没有使用原始的、未绑定的对象引用或精确的副本,则使用此方法无法可靠地确定对象的可空性。

    在大多数编码方案中,要确定可空性,必须依赖于测试原始对象的类型,而不是其引用(例如,代码必须访问对象的原始类型才能确定可空性)。在这些更常见的情况下,IstypeNullable(请参见链接)是确定可空性的可靠方法。

    P.S.-关于“空性”

    我应该在另一篇文章中重复一个关于空性的声明,它直接适用于正确地解决这个主题。也就是说,我认为这里讨论的重点不应该是如何检查一个对象是否是通用的可以为空的类型,而应该是是否可以为其类型的对象分配一个值为空。换句话说,我认为我们应该确定一个对象类型是否可以为空,而不是它是否可以为空。区别在于语义,即确定空性的实际原因,这通常是最重要的。

    在使用类型可能在运行时未知的对象(Web服务、远程调用、数据库、源等)的系统中,常见的要求是确定是否可以为该对象分配空值,或者该对象是否包含空值。在不可为空的类型上执行此类操作可能会产生错误,通常是异常,这在性能和编码要求方面都非常昂贵。为了采取主动避免此类问题的高度首选方法,有必要确定任意类型的对象是否能够包含空值,即它是否通常是“可空”的。

    在非常实际和典型的意义上,.NET术语中的可空性并不一定意味着对象的类型是可空的形式。实际上,在许多情况下,对象都有引用类型,可以包含空值,因此都可以为空;这些对象都没有可以为空的类型。因此,为了在大多数场景中实现实际目的,应该对可空性的一般概念(而不是与实现相关的可空性概念)进行测试。因此,我们不应该仅仅关注.NET可空类型,而应该在关注可空性的一般、实用概念的过程中,将我们对它的需求和行为的理解结合起来。

        8
  •  6
  •   thinkbeforecoding    16 年前

    当装箱一个可以为空的类型时,要小心。( Nullable<int> 还是int?例如:

    int? nullValue = null;
    object boxedNullValue = (object)nullValue;
    Debug.Assert(boxedNullValue == null);
    
    int? value = 10;
    object boxedValue = (object)value;
    Debug.Assert( boxedValue.GetType() == typeof(int))
    

    它将成为一个真正的引用类型,因此您将失去它可以为空的事实。

        9
  •  6
  •   sclarke81    8 年前

    我想到的最简单的解决方案是实现微软的解决方案( How to: Identify a Nullable Type (C# Programming Guide) )作为扩展方法:

    public static bool IsNullable(this Type type)
    {
        return type.IsGenericType && type.GetGenericTypeDefinition() != typeof(Nullable<>);
    }
    

    然后可以这样称呼:

    bool isNullable = typeof(int).IsNullable();
    

    这似乎也是访问 IsNullable() 因为它与所有其他 IsXxxx() 方法 Type 班级。

        10
  •  3
  •   Roel van Megen    10 年前

    也许有点离题,但还是有一些有趣的信息。我发现很多人 Nullable.GetUnderlyingType() != null 如果类型可以为空,则标识为。这显然有效,但微软建议如下 type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) (见 http://msdn.microsoft.com/en-us/library/ms366789.aspx )

    我从表演的角度看这个。下面的测试(一百万次尝试)的结论是,当类型可以为空时,Microsoft选项提供最佳性能。

    可以为空。GetUnderlyingType(): 1335MS (慢3倍)

    getGenerictypedefinition()==typeof(可以为空)(<>): 500毫秒

    我知道我们谈论的时间很短,但每个人都喜欢调整毫秒数:—)!所以如果你是老板希望你减少一些毫秒,那么这就是你的救星…

    /// <summary>Method for testing the performance of several options to determine if a type is     nullable</summary>
    [TestMethod]
    public void IdentityNullablePerformanceTest()
    {
        int attempts = 1000000;
    
        Type nullableType = typeof(Nullable<int>);
    
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
        {
            Assert.IsTrue(Nullable.GetUnderlyingType(nullableType) != null, "Expected to be a nullable"); 
        }
    
        Console.WriteLine("Nullable.GetUnderlyingType(): {0} ms", stopwatch.ElapsedMilliseconds);
    
        stopwatch.Restart();
    
        for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
       {
           Assert.IsTrue(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>), "Expected to be a nullable");
       }
    
       Console.WriteLine("GetGenericTypeDefinition() == typeof(Nullable<>): {0} ms", stopwatch.ElapsedMilliseconds);
       stopwatch.Stop();
    }
    
        11
  •  0
  •   ipavlu    9 年前

    本版本:

    • 缓存结果更快,
    • 不需要不必要的变量,比如方法(t obj)
    • 不复杂:),
    • 只是静态泛型类,它有一个一次性计算字段

    :

    public static class IsNullable<T>
    {
        private static readonly Type type = typeof(T);
        private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        public static bool Result { get { return is_nullable; } }
    }
    
    bool is_nullable = IsNullable<int?>.Result;
    
        12
  •  0
  •   Lorenz Lo Sauer    8 年前

    这就是我想到的,因为其他一切似乎都失败了——至少在 可移植类库 / 网络核心 C=6

    解决方案: 扩展任何类型的静态方法 T Nullable<T> 并使用这样一个事实:与基础类型匹配的静态扩展方法将被调用,并且优先于泛型方法 T 扩展方法。

    为了 T :

    public static partial class ObjectExtension
    {
        public static bool IsNullable<T>(this T self)
        {
            return false;
        }
    }
    

    为了 可以为空<t>

    public static partial class NullableExtension
    {
        public static bool IsNullable<T>(this Nullable<T> self) where T : struct
        {
            return true;
        }
    }
    

    使用反射和 type.IsGenericType …没有在我当前的.NET运行时集上工作。也没有 MSDN Documentation 帮助。

    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

    部分原因是反射API在.NET核心中发生了很大的变化。

        13
  •  0
  •   NetMage    6 年前

    我认为那些使用微软建议的测试 IsGenericType 很好,但在代码中 GetUnderlyingType ,Microsoft使用附加测试确保没有传入泛型类型定义。 Nullable<> :

     public static bool IsNullableType(this Type nullableType) =>
        // instantiated generic type only                
        nullableType.IsGenericType &&
        !nullableType.IsGenericTypeDefinition &&
        Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
    
        14
  •  -1
  •   VJPPaz    9 年前

    一种简单的方法:

        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);
        }