C# 如果一个或两个操作数都为空,则该值为false。否则,提升运算符将展开操作数并应用基础运算符以生成布尔结果

C# 如果一个或两个操作数都为空,则该值为false。否则,提升运算符将展开操作数并应用基础运算符以生成布尔结果,c#,.net,null,comparison-operators,C#,.net,Null,Comparison Operators,空文本本身并没有真正的类型。它是根据它被分配到的对象来推断的。然而,这里没有任务发生。只有考虑到具有语言支持的内置类型(带有关键字的类型),object或任何可为空的类型才是一个很好的选择。但是,对象不可比较,因此被排除在外。这就使得可空类型成为很好的候选者。但是哪种类型?由于左操作数和右操作数都没有指定的类型,因此默认情况下,它们将转换为(可为空的)int。由于两个可为null的值都为null,因此它返回false。规范中有许多答案。事实证明,这是一个不寻常的事件转折,C#4规范没有证明特别提

空文本本身并没有真正的类型。它是根据它被分配到的对象来推断的。然而,这里没有任务发生。只有考虑到具有语言支持的内置类型(带有关键字的类型),
object
或任何可为空的类型才是一个很好的选择。但是,
对象
不可比较,因此被排除在外。这就使得可空类型成为很好的候选者。但是哪种类型?由于左操作数和右操作数都没有指定的类型,因此默认情况下,它们将转换为(可为空的)
int
。由于两个可为null的值都为null,因此它返回false。

规范中有许多答案。事实证明,这是一个不寻常的事件转折,C#4规范没有证明特别提到的比较两个null文本的行为是正确的。事实上,对规范的严格阅读表明,“null==null”应该给出一个歧义错误!(这是由于在准备C#3时清理C#2规范时出现编辑错误所致;规范作者无意将其定为非法。)

如果你不相信我,请仔细阅读说明书。它说在int、uint、long、ulong、bool、decimal、double、float、string、enum、委托和对象上定义了相等运算符,以及所有值类型运算符的提升到可为null的版本

现在,我们马上就有问题了;这个集合是无限大的。实际上,我们不会在所有可能的委托和枚举类型上形成所有运算符的无限集。需要在这里修改规范,以注意添加到候选集的枚举和委托类型上的唯一运算符是作为任一参数类型的枚举或委托类型的运算符

因此,让我们将枚举和委托类型排除在外,因为两个参数都没有类型

我们现在有一个过载解决问题;我们必须首先消除所有不适用的运算符,然后确定适用运算符中的最佳运算符

显然,在所有不可为null的值类型上定义的运算符都不适用。这将在可空值类型、字符串和对象上保留运算符

出于“更好”的原因,我们现在可以取消一些。更好的运算符是具有更具体类型的运算符。智力?比任何其他可为空的数值类型都更具体,因此所有这些类型都被消除。字符串比对象更具体,所以对象被删除

这就留下了字符串int的相等运算符?还有布尔?作为适用的操作员。哪一个是最好的它们中没有一个比另一个好。因此,这应该是一个模糊错误

为了通过规范证明这种行为,我们必须修订规范,以注意“null==null”被定义为具有字符串相等的语义,并且它是编译时常量true

其实我昨天才发现这个事实,;你竟然问这件事,真奇怪

回答其他答案中提出的有关为什么
null>=null
的问题时,会给出与int比较的警告?--好吧,应用和我刚才一样的分析。不可为null的值类型上的
=
运算符不适用,剩下的运算符中,int上的运算符?这是最好的。
=
没有歧义错误,因为bool上没有定义
=
运算符?或字符串。编译器将运算符正确地分析为两个可为null的int的比较


要回答为什么空值上的运算符(相对于文本)具有特殊的异常行为这一更一般的问题,请参阅我对重复问题的回答。它清楚地解释了证明这一决定的设计标准。简而言之:null上的操作应该具有“我不知道”上的操作的语义。一个你不知道的量是否大于或等于另一个你不知道的量?唯一合理的答案是“我不知道!”但我们需要把它变成一个布尔,而合理的布尔是“假”。但是当比较相等时,大多数人认为null应该等于null,即使比较两件你不知道相等的事情也会导致“我不知道”。这一设计决策是权衡许多不良结果的结果,以找到最不坏的结果,从而使功能正常工作;我同意,这确实使语言有点不一致。

但是为什么
null>=null
总是错误的?这意味着
null>=null
并不等同于
null==null | | null>null
。为什么他会被否决?(希望我不会因为我在问:)而被否决@Martinho正如答案所说,
=null
总是错误的。因此,
null>=null
为false。您是正确的(“null>=null不等于null==null | | null>null”);如果这是真的,那么你就不会看到这种行为@柯克:我更好奇的是,是什么导致了这样一个决定:如果一个操作数为空,则提升的关系运算符总是产生false。为什么要“打破”这些运营商的“常规”关系?此外,编译器是否“足够聪明”来判断>=null始终为false并对其进行优化也无关紧要。不管它是否知道,它都会有这种行为,因为这是规范中所写的。@Kirk:是的,那部分正在使&&运算符短路。但不能因此说>=短路:到那时,两个操作数都已经计算过了<代码>函数f=()=>{抛出新异常();};a>=f()抛出,但
Func f=()=>{throw new Exception();};false&&f()
不存在可能只计算一个操作数,但>=始终同时计算这两个操作数。这就是短路的原因,但是其他的
null >= null
null == null 
null >= null
using System;

class Example
{
    static void Main()
    {
        int? i = null;

        Console.WriteLine(i >= null);
        Console.WriteLine(i == null);
    }
}
class Example
{
    private static void Main()
    {
        int? i = new int?();
        Console.WriteLine(false);
        Console.WriteLine(!i.HasValue);
    }
}
< > <= >=
int operator *(int x, int y);
uint operator *(uint x, uint y);
long operator *(long x, long y);
ulong operator *(ulong x, ulong y);
void operator *(long x, ulong y);
void operator *(ulong x, long y);
float operator *(float x, float y);
double operator *(double x, double y);
decimal operator *(decimal x, decimal y);
Console.WriteLine(null == null); // true
Console.WriteLine(null != null); // false
Console.WriteLine(null < null);  // false*
Console.WriteLine(null <= null); // false*
Console.WriteLine(null > null);  // false*
Console.WriteLine(null >= null); // false*
static void PrintTypes(LambdaExpression expr)
{
    Console.WriteLine(expr);
    ConstantExpression cexpr = expr.Body as ConstantExpression;
    if (cexpr != null)
    {
        Console.WriteLine("\t{0}", cexpr.Type);
        return;
    }
    BinaryExpression bexpr = expr.Body as BinaryExpression;
    if (bexpr != null)
    {
        Console.WriteLine("\t{0}", bexpr.Left.Type);
        Console.WriteLine("\t{0}", bexpr.Right.Type);
        return;
    }
    return;
}
PrintTypes((Expression<Func<bool>>)(() => null == null)); // constant folded directly to bool
PrintTypes((Expression<Func<bool>>)(() => null != null)); // constant folded directly to bool
PrintTypes((Expression<Func<bool>>)(() => null < null));
PrintTypes((Expression<Func<bool>>)(() => null <= null));
PrintTypes((Expression<Func<bool>>)(() => null > null));
PrintTypes((Expression<Func<bool>>)(() => null >= null));
() => True
        System.Boolean
() => False
        System.Boolean
() => (null < null)
        System.Nullable`1[System.Int32]
        System.Nullable`1[System.Int32]
() => (null <= null)
        System.Nullable`1[System.Int32]
        System.Nullable`1[System.Int32]
() => (null > null)
        System.Nullable`1[System.Int32]
        System.Nullable`1[System.Int32]
() => (null >= null)
        System.Nullable`1[System.Int32]
        System.Nullable`1[System.Int32]