首先:这不是How to check if an object is nullable?的副本。或者,至少,该问题没有提供有用的答案,作者的进一步阐述实际上询问了如何确定给定的类型(例如,从MethodInfo.ReturnType返回)是可空的。
然而,这很容易。困难的是确定在编译时类型未知的运行时对象是否为可空类型。考虑:
public void Foo(object obj)
{
var bar = IsNullable(obj);
}
private bool IsNullable(object obj)
{
var type = obj.GetType();
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
// Alternatively something like:
// return !(Nullable.GetUnderlyingType(type) != null);
}
不按预期工作,因为GetType()
调用导致装箱操作(https://msdn.microsoft.com/en-us/library/ms366789.aspx),并将返回基础值类型,而不是可空类型。因此IsNullable()
将始终返回false。
现在,以下技巧使用类型参数推断来获取(未装箱的)类型:
private bool IsNullable<T>(T obj)
{
var type = typeof(T);
return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
}
这似乎最初很有希望。但是,类型参数推断仅在编译时已知对象类型时才有效。所以:
public void Foo(object obj)
{
int? genericObj = 23;
var bar1 = IsNullable(genericObj); // Works
var bar2 = IsNullable(obj); // Doesn't work (type parameter is Object)
}
总之:一般问题不是确定一个类型是否可以为空,而是首先获取类型。
所以,我的挑战是:如何确定运行时对象(上例中的obj参数)是否可以为空?把我吹走了:))
答案 0 :(得分:2)
嗯,你来不及了。盒装值不再具有您搜索的类型信息 - 将int?
值装箱null
或装箱int
。在某种程度上,它类似于在GetType
上调用null
- 它没有真正意义,没有类型信息。
如果可以的话,尽可能坚持使用泛型方法而不是装箱(dynamic
对实际可空值和object
s之间的某些接口非常有帮助。如果你不能,你将不得不使用你自己的&#34;拳击&#34; - 甚至只是创建自己的Nullable
- 类型,这将是class
而不是非常hacky struct
- 类似的事情:D
如果需要,您甚至可以将Nullable
类型设为struct
。它不是打破类型信息的struct
- 它不是破坏该信息的装箱本身,它是使Nullable
能够使struct MyNullable<T>
{
private bool hasValue;
private T value;
public static MyNullable<T> FromValue(T value)
{
return new MyNullable<T>() { hasValue = true, value = value };
}
public static implicit operator T (MyNullable<T> n)
{
return n.value;
}
}
private bool IsMyNullable(object obj)
{
if (obj == null) return true; // Duh
var type = obj.GetType().Dump();
return type.IsGenericType
&& type.GetGenericTypeDefinition() == typeof(MyNullable<>);
}
的CLR黑客攻击匹配非可空值的性能。它非常聪明且非常有用,但它打破了一些基于反射的黑客攻击(就像你正在尝试做的那样)。
这可以按预期工作:
System.Nullable
对new int?(42).GetType()
执行相同的操作并非如此;即使只是System.Int32
,也会为您System.Nullable<System.Int32>
而不是System.Nullable
。
struct
不是真正的类型 - 运行时会得到特殊处理。它确实可以用你自己的类型进行复制,因为即使在IL中的类型定义中黑客也没有 - 它在CLR本身就是正确的。另一个很好的抽象泄漏是可空类型不被视为struct
- 如果您的泛型类型约束是new T?()
,则您不能使用可空类型。为什么?好吧,添加这个约束意味着使用例如合法的MyNullable
- 否则无法实现,因为你无法成为可空的可空。我在这里写的System.Nullable
类型很容易,但box
没有。
修改强>:
CLI规范的相关部分(1.8.2.4 - 装箱和拆箱值):
所有值类型都有一个名为box的操作。拳击任何值 value类型产生其盒装值;即,值的 相应的盒装类型,包含原始的按位副本 值。如果值类型是可空类型 - 定义为 实例化值类型System.Nullable-结果是a 类型为T的Value属性的null引用或按位副本, 取决于其HasValue属性(分别为false和true)。所有 盒装类型有一个名为unbox的操作,这会导致托管 指向值的位表示的指针。
因此,根据定义,对可空类型的{{1}}操作会产生空引用或存储的值,而不是&#34;盒装可为空的&#34;。