如何检查一个对象是否可以空?
如何检查一个给定的对象是否可以空的换句话说如何实现下面的方法…
bool IsNullableValueType(object o) { ... }
编辑:我正在寻找可为空值types。 我没有考虑到ref的types。
//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现在引用值为true
bool
( System.Boolean
)types的对象。 我真正想要的是一个Nullable<bool>
types的对象
所以现在作为一个解决scheme,我决定检查是否可以空,并创build一个可以包装obj的包装。
有两种types的可为空 – Nullable<T>
和引用types。
乔恩已经纠正了我很难得到types,如果盒装,但你可以用generics: – 下面怎么样。 这实际上是testingtypesT
,但是纯粹用于genericstypes推断的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 }
但是,如果您已经将值装箱到一个对象variables,这将不会很好。
有一个使用方法重载的非常简单的解决scheme
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
“如何检查一个types是否可以为空”的问题 实际上是“如何检查一个types是否为Nullable<>
?”,可以概括为“如何检查一个types是否是某种genericstypes的构造types?”,这样不仅可以回答“可为Nullable<int>
一个Nullable<>
?“,还有” List<int>
List<>
?“。
大多数提供的解决scheme使用Nullable.GetUnderlyingType()
方法,这显然只适用于Nullable<>
的情况。 我没有看到可以与任何genericstypes一起工作的通用reflection解决scheme,所以我决定在这里添加它来作为后代,即使这个问题早已被回答了。
要使用reflection检查某个types是否为Nullable<>
某种forms,首先必须将构造的genericstypes(例如Nullable<int>
)转换为genericstypes定义Nullable<>
。 你可以通过使用Type
类的GetGenericTypeDefinition()
方法来实现。 然后,您可以将结果types与Nullable<>
进行比较:
Type typeToTest = typeof(Nullable<int>); bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>); // isNullable == true
相同的可以应用于任何genericstypes:
Type typeToTest = typeof(List<int>); bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>); // isList == true
几种types可能看起来是一样的,但是不同types的参数意味着它是完全不同的types。
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
对象每个types都被实例化一次,因此可以检查它们之间的引用是否相等。 所以如果你想检查两个对象是否具有相同的genericstypes定义,你可以写:
var listOfInts = new List<int>(); var listOfStrings = new List<string>(); bool areSameGenericType = listOfInts.GetType().GetGenericTypeDefinition() == listOfStrings.GetType().GetGenericTypeDefinition(); // areSameGenericType == true
如果你想检查一个对象是否为空,而不是Type
,那么你可以使用上述技巧和Marc Gravell的解决scheme来创build一个相当简单的方法:
static bool IsNullable<T>(T obj) { if (!typeof(T).IsGenericType) return false; return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>); }
那么,你可以使用:
return !(o is ValueType);
…但是一个对象本身不是空的或者是其他types的。 你打算如何使用这个?
我能弄明白的最简单的方法是:
public bool IsNullable(object obj) { Type t = obj.GetType(); return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); }
这对我而言似乎很简单:
static bool IsNullable<T>(T obj) { return default(T) == null; }
这里有两个问题:1)testing一个types是否为空; 2)testing一个对象是否代表可空types。
对于问题1(testingType),下面是我在自己的系统中使用的解决scheme: TypeIsNullable-check解决scheme
对于问题2(testing对象),Dean Chalk上面的解决scheme适用于值types,但对于引用types不起作用,因为使用<T>重载总是返回false。 由于引用types本质上是可空的,因此testing引用types应始终返回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; }
以下是我对上述解决scheme的客户端testing代码的修改:
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的方法的原因是他的原始方法总是返回一个引用types的false。 由于像IsObjectNullable这样的方法应该能够处理引用types的值,并且由于所有的引用types本质上都是可以为空的,所以如果引用types或者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>types参数时,执行此单一方法的最后一行需要更多的处理器时间,而不是编译器select之前显示的第二个方法重载。 因此,最佳解决scheme是使用这里所示的双方法。
CAVEAT:只有在使用原始对象引用或精确副本进行调用时,此方法才能可靠地工作,如示例中所示。 但是,如果可空对象被装箱到另一个types(如对象等),而不是保留在其原始Nullable <>forms中,则此方法将无法可靠地工作。 如果调用此方法的代码未使用原始的,取消装箱的对象引用或精确副本,则无法使用此方法可靠地确定对象的可空性。
在大多数编码场景中,为了确定可空性,必须依赖testing原始对象的types,而不是其引用(例如,代码必须访问对象的原始types以确定可空性)。 在这些更常见的情况下,IsTypeNullable(参见链接)是确定可空性的可靠方法。
PS – 关于“可空性”
我应该在一个单独的post中重复关于可空性的陈述,这个陈述直接适用于正确地处理这个话题。 也就是说,我相信这里讨论的焦点不应该是如何检查一个对象是否是一个通用的Nullabletypes,而是一个可以赋值为null的types的对象。 换句话说,我认为我们应该确定一个对象types是否可以为null,而不是Nullable。 不同之处在于语义,即决定可空性的实际原因,这通常是重要的。
在一个系统中使用的对象的types可能直到运行时(web服务,远程调用,数据库,提要等等)都是未知的,一个常见的要求是确定一个null是否可以被分配给该对象,或者该对象是否可能包含一个null。 对不可空的types执行这样的操作可能会产生错误,通常是例外,在性能和编码要求方面都是非常昂贵的。 为了采取主动避免这种问题的高度优选的方法,有必要确定任意types的对象是否能够包含空值; 即它是否一般是“可空的”。
在一个非常实际和典型的意义上,.NET中的可空性并不一定意味着一个对象的Type是一个可为空的forms。 事实上在很多情况下,对象具有引用types,可以包含空值,因此都是可以为空的; 这些都没有可空types。 因此,在大多数情况下,为了实际的目的,应该针对可空性的一般概念进行testing,而对于可执行相关的Nullable概念进行testing。 所以我们不应该把注意力集中在.NET Nullabletypes上,而是把我们对它的需求和行为的理解纳入关注可空性的一般的,实用的概念的过程中。
请小心,当装箱nullabletypes(例如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))
它成为一个真正的参考types,所以你失去了它可以为空的事实。
也许有点脱离主题,但仍然有一些有趣的信息。 我发现很多人使用Nullable.GetUnderlyingType() != null
来识别,如果一个types是可空的。 这显然有效,但Microsoftbuild议下列typestype.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)
(请参阅http://msdn.microsoft.com/en-us/library/ms366789.aspx )。
我从性能方面来看这个。 下面的testing(一百万次尝试)的结论是,当一个types是可空的,微软的选项提供了最好的性能。
Nullable.GetUnderlyingType(): 1335ms (慢3倍)
GetGenericTypeDefinition()== typeof(Nullable <>): 500ms
我知道我们正在谈论less量的时间,但是每个人都喜欢调整毫秒:-)! 所以,如果你是老板要你减less一些毫秒,那么这是你的救星…
/// <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(); }
我想到的最简单的解决scheme是实现Microsoft的解决scheme( 如何:确定可空types(C#编程指南) )作为扩展方法:
public static bool IsNullable(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() != typeof(Nullable<>); }
这可以这样调用:
bool isNullable = typeof(int).IsNullable();
这似乎也是访问IsNullable()
一种合乎逻辑的方式,因为它适合Type
类的所有其他IsXxxx()
方法。
这个版本:
- caching结果更快,
- 不需要不必要的variables,如Method(T obj)
- 不复杂:),
- 只是静态generics类,它有一次计算字段
:
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;
这是我想到的,因为一切似乎都失败了 – 至less在PLC上 – > = C#6的可移植类库 / .NET核心
解决scheme:扩展任何Type T
和Nullable<T>
的静态方法,并使用匹配底层types的静态扩展方法将被调用并优先于genericsT
扩展方法的事实。
对于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; } }
使用reflection和type.IsGenericType
…不工作在我当前的一组.NET运行时。 MSDN文档也没有帮助。
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}
部分原因是在.NET Core中Reflection API已经发生了相当大的变化。
一个简单的方法来做到这一点:
public static bool IsNullable(this Type type) { if (type.IsValueType) return Activator.CreateInstance(type) == null; return true; }
这些是我的unit testing,全部通过
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
实际的unit testing
[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); }