为什么不能使用is运算符来区分bool和Nullable <bool>?

Ada*_*dam 13 c# boxing types

我偶然发现了这一点,我很好奇为什么不能用is操作员辨别boolNullable<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);
}
Run Code Online (Sandbox Code Playgroud)

打电话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!
Run Code Online (Sandbox Code Playgroud)

我期待;

Considering theBool:
    It's a bool!
Considering theNullableBoolThatsFalse:
    It's a Nullable<bool>!
Considering theNullableBoolThatsNull:
    It's a null!
Run Code Online (Sandbox Code Playgroud)

为什么这两个boolNullable<bool>相互匹配?

我试过了什么;

  • 我请教了文档Nullable,is,switch和模式匹配.
  • 我认为这可能与传递给方法时的值的拆箱有关?

我认为它可能是独一无二的,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);
}
Run Code Online (Sandbox Code Playgroud)

给;

Considering theBool:
    It's a bool!
Considering theListOfBool:
    It's a List<bool>
Run Code Online (Sandbox Code Playgroud)

我不打算解决问题.只是感兴趣为什么它这样工作.

到目前为止,答案表明它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);
}
Run Code Online (Sandbox Code Playgroud)

给;

Considering a;
    It's a A!
Considering b;
    It's a B!
Run Code Online (Sandbox Code Playgroud)

文档is说:

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

框架决定参考转换,装箱转换和拆箱转换吗?

Ser*_*rvy 8

传递给你的方法的原因boolNullable<bool>行为是因为每当你Nullable<T>打包它时它实际上并没有包装可空值,而是打开可空和值的值.如果空值是零,那么你最终只null,而不是盒装的Nullable<T>地方HasValuefalse.

如果你框一个非空值,它只会框中ValueNullable<T>.因此,从视角来看WhatIsIt,前两个调用在字面上是无法区分的,因为传递完全相同的值.

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

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

在这种情况下,这是考虑E is T并且D在前面定义为其中的计算值E:

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

这意味着is操作符被明确定义为将可空类型视为等同于其基础类型,无论您如何混合和匹配正在检查的实际值以及您使用可空值和可空的底层类型检查的类型.


Him*_*ere 5

该值false可以安全地转换为两者bool,bool?因为它们之间存在隐式转换运算符.

null另一方面无法转换bool,这就是为什么要null is bool返回false.

is运营商不(和不可阻挡的)关心你如何声明变量-如果在所有.它仅指示运行时提供的类型.你也可以这样写:

WhatIsIt(false)
Run Code Online (Sandbox Code Playgroud)

您希望这种方法在这里表现如何?它只是尝试将值转换为两种类型 - 它可以 - 然后为两者返回true.

为什么它对其他泛型不起作用只是因为在最通用的类​​型和它们的类型参数之间没有隐式转换.因此以下不起作用:

string myString = new List<string>();
Run Code Online (Sandbox Code Playgroud)

  • 根据其[文档](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/is),`is`运算符不考虑隐式转换:`3是尽管C#语言定义了从`int`到`double`的隐式转换,但double`返回`false`. (2认同)