可空对象必须具有值。

266

异常描述存在悖论:

可空对象必须具有值(?!)

问题如下:

我有一个名为DateTimeExtended的类,该类有一个

{
  DateTime? MyDataTime;
  int? otherdata;

}

以及一个构造函数

DateTimeExtended(DateTimeExtended myNewDT)
{
   this.MyDateTime = myNewDT.MyDateTime.Value;
   this.otherdata = myNewDT.otherdata;
}

运行这段代码。

DateTimeExtended res = new DateTimeExtended(oldDTE);

抛出一个带有以下信息的 InvalidOperationException

可空对象必须具有值。

myNewDT.MyDateTime.Value - 是有效的且包含常规的 DateTime 对象。

这个消息的含义是什么?我做错了什么?

请注意,oldDTE 不是 null。我已从 myNewDT.MyDateTime 中删除了 Value,但由于生成的 setter,仍会抛出相同的异常。


其他构造函数是什么? - Paul Creasey
它是通过不带参数的new()构造的,然后我添加了值(它可以工作)。 - Dani
@Yuliy - 如果我先尝试设置otherdata字段,我会得到相同的异常。 - Dani
@Yuliy - 我在一个新项目中运行了这段代码,它像你说的那样工作。我正在努力弄清楚出了什么问题... - Dani
6
问题已解决 - 问题原本并不存在...实际上是在对otherdata和MyDateTime生成了setter时,对值进行了检查。当该值为null时,它就会失效! - Dani
显示剩余3条评论
11个回答

260
你应该将这一行代码 this.MyDateTime = myNewDT.MyDateTime.Value; 改为 this.MyDateTime = myNewDT.MyDateTime; 你之前遇到的异常是由于在可空类型 Nullable DateTime.Value 属性中抛出,因为它需要返回一个 DateTime(因为这是.Value的契约),但是却无法这样做,因为没有DateTime 可以返回,所以会抛出异常。
通常情况下,在对可空类型进行运算时盲目调用.Value是不好的,除非您有一些先前的知识表明该变量必须包含值(例如通过使用.HasValue检查)。 编辑 这里是DateTimeExtended代码,它不会抛出异常:
class DateTimeExtended
{
    public DateTime? MyDateTime;
    public int? otherdata;

    public DateTimeExtended() { }

    public DateTimeExtended(DateTimeExtended other)
    {
        this.MyDateTime = other.MyDateTime;
        this.otherdata = other.otherdata;
    }
}

我这样测试:

DateTimeExtended dt1 = new DateTimeExtended();
DateTimeExtended dt2 = new DateTimeExtended(dt1);

other.MyDateTime上添加.Value会导致异常,删除它将消除该异常。我认为你正在错误的地方寻找。


你关于.value的说法是正确的,但还有其他原因导致了异常。 我已经删除了.value,并且改变了构造函数的代码顺序-首先复制int值,但仍然抛出相同的异常。 - Dani
我已经对问题进行了评论-找到了问题所在,它位于为属性生成的setter方法中。 - Dani
是的,我的问题已经解决了,我只是将可空对象更改为非可空对象,并直接将日期时间转换为字符串,而不是通过datetimeobject.value.datetime.tostring()。 - adnan
很棒的答案。在空对象上调用.Value时出现异常是有道理的(我猜),但如果你正在处理两个可空对象,异常消息确实会让人误解。类似“该.Value属性要求对象非空”这样的消息会更加合理。 - DVK

26

使用LINQ扩展方法(例如SelectWhere)时,lambda函数可能会转换为SQL,这可能与您的C#代码的行为不完全相同。例如,C#的短路求值&&||会转换为SQL的贪婪ANDOR。当您在lambda中检查null时,这可能会导致问题。

示例:

MyEnum? type = null;
Entities.Table.Where(a => type == null || 
    a.type == (int)type).ToArray();  // Exception: Nullable object must have a value

19
我明白这个回答与提问者的具体情况无关,但它与他所得到的异常相关。此外,这个页面是谷歌搜索该异常的第一个结果,因此是相关的。 - Protector one
这对我来说也是一个问题,涉及到空条件运算符(JavaScript中的“可选链”)。与其使用obj?.getValue(),我需要使用obj != null && obj.getValue()。 - BobtheMagicMoose

8
尝试删除.value
DateTimeExtended(DateTimeExtended myNewDT)
{
   this.MyDateTime = myNewDT.MyDateTime;
   this.otherdata = myNewDT.otherdata;
}

如果我先运行第二行,它仍然会抛出相同的异常,这并没有帮助。 - Dani

2

直接分配成员,无需使用.Value部分:

DateTimeExtended(DateTimeExtended myNewDT)
{
   this.MyDateTime = myNewDT.MyDateTime;
   this.otherdata = myNewDT.otherdata;
}

2
在这种情况下,oldDTE为null,因此当您尝试访问oldDTE.Value时,由于没有值,会引发InvalidOperationException异常。在您的示例中,您可以简单地执行以下操作:
this.MyDateTime = newDT.MyDateTime;

oldDTE 不是空的,但是我已经将它的值删除了...... 它仍然抛出异常.... - Dani

2

回答你的实际问题,“Nullable object must have a value”是什么意思?

我认为这是一个可怕的错误,它的意思是“你正在尝试获取一个可空对象的.Value,但它是null。”

或者他们也可以说“必须给可空对象赋值才能获取它的.Value”。


2
我同意异常信息写得非常糟糕。可空对象的核心就是它不必具有值! - Eike

1

使用

`DateTimeExtended(DateTimeExtended myNewDT)
{
   this.MyDateTime = myNewDT.MyDateTime.**GetValueOrDefault();**
   this.otherdata = myNewDT.otherdata;
}`

它只会检查数值并在没有数值的情况下设置为Null。
> .Value

只有在你确定它会出现的情况下才能使用。 但是既然你不确定,就简单地使用".GetValueOrDefault()"。

感谢您对Stack Overflow社区做出贡献的兴趣。这个问题已经有很多答案了,其中一个答案已经得到社区广泛验证。您确定您的方法之前没有被提到过吗?如果是这样的话,能否解释一下您的方法与众不同的地方,在什么情况下您的方法可能更好,并且为什么您认为之前的答案不够满意。您可以编辑您的回答并提供解释吗? - Jeremy Caney

0

看起来 oldDTE.MyDateTime 是 null,所以构造函数试图获取它的值 - 这导致了异常。


0

当我尝试访问一个空值对象的值时,我收到了这个消息。

sName = myObj.Name;

这会产生错误。首先,您应该检查对象是否为 null

if(myObj != null)
  sName = myObj.Name;

这个有效。


2
在回答问题之前,请尝试先阅读其他答案,特别是已接受的答案,它明确说明了你在你的答案中提到的内容。虽然它没有使用代码显示,但它已经解释清楚了。此外,请尝试使您的示例代码与问题相关 - 例如this.MyDateTime = myNewDT.MyDateTime.Value;而不是sName = myObj.Name; - newfurniturey

0

我得到了这个解决方案,它对我起作用。

if (myNewDT.MyDateTime == null)
{
   myNewDT.MyDateTime = DateTime.Now();
}

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