为什么/ .Net允许我否定一个空的Nullable <T>?

Chr*_*tte 5 .net nullable

我发现自己正盯着看起来与此类似的代码:

private double? _foo;

public double? Foo
{
    get { return _foo; }
    set { Baz(-value); }
}

public void Baz(double? value)
{
    // Perform stuff with value, including null checks
}
Run Code Online (Sandbox Code Playgroud)

在测试中,我有这个:

[Test]
public void Foo_test()
{
    ...
    Foo = null;
    ...
}
Run Code Online (Sandbox Code Playgroud)

我预计测试会失败(抛出),因为我认为你真的不能应用一元减号null,我认为这会在Foos setter中发生.然而,意想不到的事情发生,并测试并没有失败.

我可能会对这是为什么会发生这种情况做出明智的猜测,但我认为如果知道发生了什么事的人可以启发我会更好.

(并且在有人指出显而易见的事实之前:是的,这种否定应该被推到内部Baz.)

编辑并跟进

所以,我从乔恩斯基特和JaredPar,谁回答都将投入感谢如何,我的问题的一部分.经过一些谷歌搜索后,我发现Eric Lippert的这篇博客文章解释了"提升"的数学定义,这非常有意义.我还阅读了语言规范的相关部分,这些部分非常简单地解释了c#中提升的机制.

具体来说,我现在知道在提升过程的早期,对操作数进行空检查,如果操作数为null,则null将是应用运算符的结果(在我的情况下是一元减号).所以-nullnull.

现在,问题的"为什么"部分.如果我operator -在a上定义我自己的(一元)class(在其中我否定一个包装的值),并在空引用上调用此运算符,我将得到一个NullReferenceException.为什么"提升"表现不同?我实际上对这个设计决策背后的推理感兴趣.我绝对反对这个决定.

Eri*_*ert 12

为什么"提升"表现不同?我实际上对这个设计决策背后的推理感兴趣.

好吧,首先,你不应该总是得到一个空的引用异常.静态调度操作员调用; 它不需要进行空检查.这是完全合法的:

class P
{
  public static P operator -(P p)
  { return p; }

  static void Main()
  {
    P p1 = null;
    P p2 = -p1;
    Console.WriteLine(p2 == null);
  }
}
Run Code Online (Sandbox Code Playgroud)

真的,我觉得你的问题的意思是"你为什么空值类型得到自动从非可空吊装作业可空操作,但引用类型要求你编写特定的逻辑操作,如果你想为空的语义?"

这有点乱.

问题是,引用类型始终可以为空,因此您可以自己编写该代码.由于空值类型是后来添加,我们增加了一个机制,使所有现有非空的经营者突然空类型正常工作,这样你就不用自己写那些无聊的代码.

但是有一个更普遍的问题.更普遍的问题是我们将"null"混为一谈意味着两件事.在引用类型世界中,null表示"我不引用任何对象".在值类型世界中,null表示它在数据库中的含义:此数量可能具有值但我们不知道它是什么.如果所有销售人员都没有报告他们的结果,11月的销售数字是多少?肯定有一个美元价值,但我们不知道它是什么,所以我们将其标记为空.

提升算术旨在处理"数据库空"语义; null加十二为空.你不知道的东西加上十二是你不知道的其他东西.不幸的是,由于我们没有在C#中添加可空算术直到版本2,因此已经存在将null视为"有一个引用无对象的对象引用"语义的所有设备.

如果我们设计从无到有的整个事情,我怀疑的是,(1)将有可空引用类型,非可空引用类型,空值类型和非空值类型,和(2)将有或者是更明确的界定差异在null引用和null值之间,或者它们的语义在算术时更加一致,以及(3)将所有非可空操作自动提升为可空操作.

请注意,VB/VBScript通过具有两个单独的值来实现更明确的区别:Null(数据库为null)和Nothing(引用为null).


Jon*_*eet 9

它可以否定它,因为这是"提升"一元运算符的定义方式......如果执行任何操作,如否定,加法,减法等,并且任一操作数为空,结果也为空.

有关更多详细信息,请参阅语言规范的第7.3.7节.

在这种特殊情况下:

对于一元运算符(+,++, - , - ,!,〜),如果操作数和结果类型都是非可空值类型,则存在提升形式的运算符.提升形式是通过添加一个?操作数和结果类型的修饰符.如果操作数为null,则提升的运算符将生成空值.否则,提升的运算符解包操作数,应用基础运算符,并包装结果.

请注意,这是特定于您正在使用的语言 - 特别是&和| 运算符bool?在C#中的值与在VB中的值不同.