首页 > 解决方案 > 为什么不能使用 is 运算符来区分 bool 和 Nullable?

问题描述

我遇到了这个问题,很好奇为什么不能使用is操作符来区分booland Nullable<bool>?例子;

void Main()
{
    bool theBool = false;
    Nullable<bool> theNullableBoolThatsFalse = false;
    Nullable<bool> theNullableBoolThatsNull = null;

    void WhatIsIt(object value)
    {
        if(value is bool)
            Console.WriteLine("    It's a bool!");
        if(value is Nullable<bool>)
            Console.WriteLine("    It's a Nullable<bool>!");
        if(value is null)
            Console.WriteLine("    It's a null!");
    }

    Console.WriteLine("Considering theBool:");
    WhatIsIt(theBool);
    Console.WriteLine("Considering theNullableBoolThatsFalse:");
    WhatIsIt(theNullableBoolThatsFalse);
    Console.WriteLine("Considering theNullableBoolThatsNull:");
    WhatIsIt(theNullableBoolThatsNull);
}

呼唤Main()给予;

Considering theBool:
    It's a bool!
    It's a Nullable<bool>!
Considering theNullableBoolThatsFalse:
    It's a bool!
    It's a Nullable<bool>!
Considering theNullableBoolThatsNull:
    It's a null!

我希望;

Considering theBool:
    It's a bool!
Considering theNullableBoolThatsFalse:
    It's a Nullable<bool>!
Considering theNullableBoolThatsNull:
    It's a null!

为什么两者都bool相互Nullable<bool>匹配?

我尝试了什么;

我认为它可能是独一无二的,Nullable因为我没有遇到其他泛型类型的相同问题。例如;

void Main()
{
     bool theBool = false;
     List<bool> theListOfBool= new List<bool>();    

     void WhatIsIt(object value)
     {
         if(value is bool)
             Console.WriteLine("    It's a bool!");
         if(value is List<bool>)
             Console.WriteLine("    It's a List<bool>!");
     }

     Console.WriteLine("Considering theBool:");
     WhatIsIt(theBool);
     Console.WriteLine("Considering theListOfBool:");
     WhatIsIt(theListOfBool);
}

给;

Considering theBool:
    It's a bool!
Considering theListOfBool:
    It's a List<bool>

我不是要解决问题。只是对它为什么以这种方式工作感兴趣。

到目前为止的答案表明它implicitexplicit转换导致了这种行为,但我无法用下面的例子进行复制;

class A
{
    public static implicit operator A(B value) => new A();
    public static explicit operator B(A value) => new B();
}

class B
{
    public static implicit operator A(B value) => new A();
    public static explicit operator B(A value) => new B();
}

static void Main(string[] args)
{
    var a = new A();
    var b = new B();

    void WhatIsIt(object value)
    {
        if (value is A)
            Console.WriteLine("    It's a A!");
        if (value is B)
            Console.WriteLine("    It's a B!");
    }

    Console.WriteLine("Considering a;");
    WhatIsIt(a);
    Console.WriteLine("Considering b;");
    WhatIsIt(b);
}

给;

Considering a;
    It's a A!
Considering b;
    It's a B!

文档is说:

它只考虑引用转换、装箱转换和拆箱转换;它不考虑用户定义的转换或由类型的隐式和显式运算符定义的转换。以下示例生成警告,因为转换的结果在编译时是已知的。请注意,从 int 到 long 和 double 的转换的 is 表达式返回 false,因为这些转换由隐式运算符处理。

参考转换、装箱转换和拆箱转换是框架决定的吗?

标签: c#typesboxing

解决方案


传递给您的方法时的原因boolNullable<bool>行为相同是因为每​​当您装箱时,Nullable<T>它实际上并没有装箱可空值,而是解开可空值并将其装箱。如果可为空的值为 null ,那么您最终会得到 just null,而不是装箱的Nullable<T>where HasValueis false

如果你装箱一个非空值,它只会装箱Value. Nullable<T>所以从角度来看WhatIsIt,前两个调用实际上是无法区分的,因为传入的是完全相同的值

这只是留下了为什么两个 is检查都返回的问题true,即使在这两种情况下传入的都是一个装箱的布尔值,而不是一个Nullable<T>. C# 语言规范第 7.10.10 节回答了这一点:

如果 T 是可空类型,则如果 D 是 T 的基础类型,则结果为真。

在这种情况下,这是考虑E is TD在前面定义为Ewhere 的计算值:

如果 E 的类型是可空类型,则 D 是该可空类型的基础类型。

这意味着该is运算符被明确定义为将可空类型视为等同于它们的基础类型,无论您如何混合和匹配正在检查的实际值以及您使用可空值检查的类型和该可空值的基础类型。


推荐阅读