检测可空类型

3

当一个 Nullable 类型(转换为对象时)为空时,是否可能检测到它?

由于 Nullable<T> 实际上是一个结构体,我认为这应该是可能的。

double? d = null;
var s = GetValue(d); //I want this to return "0" rather than ""

public string GetValue(object o)
{
    if(o is double? && !((double?)o).HasValue) //Not working with null
       return "0";
    if(o == null)
       return "";
    return o.ToString();
}  

如果一个接受object类型参数的例程被传递了一个Nullable<double>类型的变量,那么该例程将会收到一个空对象引用(如果变量为 null)或者对 System.Double 的引用(如果该变量不为 null)。它不会封箱Nullable<System.Double>的实例。 - supercat
5个回答

5

http://msdn.microsoft.com/zh-cn/library/ms228597(v=vs.80).aspx

基于可空类型的对象只有在对象非空时才会装箱。如果 HasValue 为 false,则对象引用被简单地分配为 null,而不是装箱。

以及

如果对象非空——如果 HasValue 为 true ——则进行装箱,但只有基于的可空对象下层的类型才会被装箱。

因此,您要么拥有一个 double 类型的值,要么是 null

public string GetValue(object o)
{
    if(o == null) // will catch double? set to null
       return "";

    if(o is double) // will catch double? with a value
       return "0";

    return o.ToString();
} 

所以答案是否定的,很遗憾这是不可能的。感谢您的解释。 - Magnus
@Magnus 但请记住Skeet告诉你的话。有一种方法,但不是通过强制转换。最终一切取决于你想要做什么。 - xanatos

5
你对于每个空值类型都有一个GetValueOrDefault方法,这难道不够吗?

由于我在函数中使用了 object,所以我需要将其转换为 Nullable<T> 才能使用该函数(就像我的示例一样)。 - Magnus
1
你不需要使用函数,可以使用GetDateOrDefault().ToString()方法。 - remi bourgarel
是的,但这是一个适用于所有类型的通用格式化函数。我想能够检测所有可空数值类型是否为空并返回“0”。 - Magnus
所有“可空类型”都不是数字(例如GUID)。 - remi bourgarel
不是,但那些是我感兴趣的(例如 double?就像例子中一样) - Magnus

3

您的方法当前接受一个对象,这意味着可为空的值将被装箱...并且不再是可空值。变量o的值可能会是非可空类型的装箱值,或者是null引用。

如果可能的话,请将您的方法改为泛型:

public string GetValue<T>(T value)
{
    // Within here, value will still be a Nullable<X> for whatever type X
    // is appropriate. You can check this with Nullable.GetUnderlyingType
}

为了更清晰地表达,这意味着在内存中,一个被装箱的可空类型和一个空引用之间没有区别。因此,在方法内部无法判断。 - Iravanchi
2
@Iravanchi:我更强烈地表达一下:根本就不存在所谓的可空装箱类型。将可空类型值装箱的结果要么是装箱非可空类型,要么是一个 null 引用。 - Jon Skeet
谢谢你的回答。不幸的是,在对象到达GetValue函数之前,它已经被装箱了,所以我不能使用你的通用示例。 - Magnus
@Magnus:在这种情况下,无法检测到最初为 null 的 double? 值和普通 null 引用之间的差异。 - Jon Skeet

0
如果 onull,那么 o is double? 将会是 false。无论你的输入参数 double? d 的值是什么。

-1
据我所知,如果您想检测任何对象是否可为空,这可以相当容易地编写。

试试这个...

public static bool IsNullable(dynamic value)
{
    try
    {
        value = null;
    }
    catch(Exception)
    {
        return false;
    }
    return true;
}

简单!


即使调用IsNullable(5),语句value = null也会覆盖类型为object的存储位置,而不是类型为int的存储位置。 - supercat
你说得对。事后看来,我没有仔细检查这个问题。理想情况下,最优雅的解决方案(至少在.NET中)应该检查对象是类还是结构,并确定对象的“nullability”。我相信我的理解是正确的,即结构体不能为null,因为它们是存在于堆栈上的值类型,而类是存在于堆上的引用类型,因此是引用而不是绝对值。我现在要去进一步调查。 - Matthew Layton
在 .net 中,有三种可以转换为 Object 的东西:可空值类型、非可空值类型和对象。请注意,值类型不是对象,即使每个非可空值类型都有一个相关的“装箱”对象类型,它可以隐式地转换为其关联的对象类型,并且每个非空可空值类型都可以隐式地转换为与其非可空等效项关联的对象类型。C# 以这样的方式定义“派生”,使得值类型“派生”自 Object,但它们并不像其他继承类类型从它们的基类中定义的那样这样做。 - supercat
定义继承时,如果X继承自Y,则将类型为X的变量xx复制到类型为Y的变量yy中,使yy引用与xx相同的对象实例是很有用的,因此Object.ReferenceEquals(xx,yy)将为true。以这种方式定义“继承”,始终应用上述行为,并说未装箱的值类型不继承自“Object”,但装箱的值类型会更有帮助,而不是说所有值类型都派生自“Object”,但某些继承关系的行为不像正常情况。 - supercat
分别非空值类型、可空值类型和对象之间的区别,适用于存储位置(变量、参数、字段或数组槽),而不是实例。这些种类的存储位置将始终保存该种类的内容或(对于后两种种类)null。但是,通用类型参数可以引用其中任何一种类型的内容。请注意,接口类型的存储位置始终是对象引用,但是被限制为接口的泛型类型可以是值类型或类类型。 - supercat
我要回学校了!这一下子让我有点难以理解。 - Matthew Layton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接