223

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

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

4

14 回答 14

297

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

乔恩纠正了我,如果装箱很难输入,但你可以使用泛型: - 那么下面怎么样。这实际上是在测试 type 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
}

但是,如果您已经将值装箱到对象变量中,这将不会那么好。

微软文档:https ://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type

于 2008-12-17T14:20:52.930 回答
47

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

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
于 2010-11-09T08:50:55.600 回答
40

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

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);
}
于 2014-04-30T20:52:20.030 回答
29

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

大多数提供的解决方案都使用该Nullable.GetUnderlyingType()方法,这显然只适用于Nullable<>. 我没有看到适用于任何泛型类型的通用反射解决方案,因此我决定在此处添加它以供后代使用,即使这个问题很久以前就已经得到解答。

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

如果您想检查一个对象是否可以为空,而不是 a Type,那么您可以将上述技术与 Marc Gravell 的解决方案一起使用来创建一个相当简单的方法:

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

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}
于 2011-01-03T11:58:03.667 回答
20

好吧,你可以使用:

return !(o is ValueType);

...但是对象本身不能为空或其他 -类型是。你打算如何使用它?

于 2008-12-17T14:21:45.110 回答
11

我能想到的最简单的方法是:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
于 2012-03-15T02:10:23.990 回答
10

这里有两个问题: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 类型,而是将我们对其要求和行为的理解融入到关注可空性的一般实用概念的过程中。

于 2011-10-14T00:27:32.057 回答
10

我想出的最简单的解决方案是实现微软的解决方案(如何:识别可空类型(C# 编程指南))作为扩展方法:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

然后可以这样调用:

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

这似乎也是一种合乎逻辑的访问方式,IsNullable()因为它适合该类的所有其他IsXxxx()方法Type

于 2016-12-22T11:00:36.493 回答
6

请注意,在装箱可空类型(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))

它变成了一个真正的引用类型,所以你失去了它可以为空的事实。

于 2008-12-17T16:10:04.033 回答
3

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

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

Nullable.GetUnderlyingType(): 1335 毫秒(慢 3 倍)

GetGenericTypeDefinition() == typeof(Nullable<>): 500ms

我知道我们谈论的是一小段时间,但每个人都喜欢调整毫秒:-)!因此,如果您的老板希望您减少几毫秒,那么这就是您的救星...

/// <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();
}
于 2014-07-04T15:32:15.463 回答
2

我认为使用微软建议的测试的IsGenericType那些很好,但是在代码中GetUnderlyingType,微软使用了一个额外的测试来确保你没有通过泛型类型定义Nullable<>

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
于 2019-01-21T20:43:31.470 回答
0

这个版本:

  • 缓存结果更快,
  • 不需要不必要的变量,例如 Method(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;
于 2015-11-10T20:05:22.583 回答
0

这就是我想出的,因为其他一切似乎都失败了——至少在PLC 上——可移植类库/.NET Core with >= C# 6

解决方案:为任何类型扩展静态方法,TNullable<T>使用与底层类型匹配的静态扩展方法将被调用并优先于通用T扩展方法这一事实。

对于T

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

并且对于Nullable<T>

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

使用反射和type.IsGenericType... 不适用于我当前的 .NET 运行时集。MSDN 文档也没有帮助。

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

部分原因是 .NET Core 中的反射 API 发生了相当大的变化。

于 2016-08-25T06:27:47.337 回答
-1

一个简单的方法来做到这一点:

    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);
    }
于 2015-08-18T03:31:24.587 回答