C# Null条件和ToString一起给出意外的结果

C# Null条件和ToString一起给出意外的结果,c#,null-conditional-operator,C#,Null Conditional Operator,我有两条语句使用null条件(?)运算符并对结果执行ToString。这两条语句似乎应该有相同的结果,但它们没有。唯一不同的是一个包含括号,而另一个不包含括号 using System.Diagnostics; using System.Net; namespace ByPermutationConsole { class Program { static void Main(string[] args) { SomeCl

我有两条语句使用null条件(?)运算符并对结果执行ToString。这两条语句似乎应该有相同的结果,但它们没有。唯一不同的是一个包含括号,而另一个不包含括号

using System.Diagnostics;
using System.Net;

namespace ByPermutationConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            SomeClass someClass = default(SomeClass);

            // Why do these evaluate differently?
            //
            // (someClass?.StatusCode).ToString() is equal to an empty string
            //
            // someClass?.StatusCode.ToString() is equal to null
            //
        }
    }

    public class SomeClass
    {
        public HttpStatusCode StatusCode { get; set; }
    }
}
我希望这两个语句的计算结果相同

(someClass?.StatusCode).ToString() == someClass?.StatusCode.ToString()
但是,它们没有:

(someClass?.StatusCode).ToString()
等于
string.Empty


someClass?.StatusCode.ToString()
等于
null

someClass?.StatusCode
计算为
null
<空
Nullable
上的code>ToString
会导致空字符串


someClass?.StatusCode.ToString()
将整个表达式短路到
null

通过使用paren,您实际上是在分解整个表达式。

您的期望是错误的(这是显而易见的),但让我解释一下原因

someClass?.StatusCode.ToString()
.........^ here the evaluation is already concluded to null - someClass is null

(someClass?.StatusCode).ToString()
.......................^ null to string will result in an empty string

查看代码中进行求值的点(^)。如果没有括号,计算将立即停止(它将永远不会停止)。使用括号进行求值,然后执行ToString

正如Daniel所说,这是动态创建的类型可为null的结果。我应该考虑一下

如前所述,不带括号的语句基本上停止求值,因为在?的右边有任何内容?。(短路)如果?之前的内容变为空?。是空的。我理解操作员在这方面的工作方式

带括号的语句强制创建可为null类型的实例,因此调用ToString会产生空字符串。我没有想到这一点,尽管这可能是我应该想到的

我使用LINQPad获取IL并验证所有这些。我本来应该这么做的。对不起,浪费了大家的时间。不过我很感激你的回答。多谢各位

class Program
{
    static void Main(string[] args)
    {
        SomeClass someClass1 = default(SomeClass);
        string result1 = someClass1?.SomeNumber.ToString();

        SomeClass someClass2 = default(SomeClass);
        string result2 = (someClass2?.SomeNumber).ToString();
    }
}

public class SomeClass
{
    public int SomeNumber { get; set; }
}



IL_0000:  nop
IL_0001:  ldnull
IL_0002:  stloc.0     // someClass1
IL_0003:  ldloc.0     // someClass1
IL_0004:  brtrue.s    IL_0009
IL_0006:  ldnull
IL_0007:  br.s        IL_0018
IL_0009:  ldloc.0     // someClass1
IL_000A:  call        UserQuery+SomeClass.get_SomeNumber
IL_000F:  stloc.s     04
IL_0011:  ldloca.s    04
IL_0013:  call        System.Int32.ToString
IL_0018:  stloc.1     // result1
IL_0019:  ldnull
IL_001A:  stloc.2     // someClass2
IL_001B:  ldloc.2     // someClass2
IL_001C:  brtrue.s    IL_002A
IL_001E:  ldloca.s    05
IL_0020:  initobj     System.Nullable<System.Int32>
IL_0026:  ldloc.s     05
IL_0028:  br.s        IL_0035
IL_002A:  ldloc.2     // someClass2
IL_002B:  call        UserQuery+SomeClass.get_SomeNumber
IL_0030:  newobj      System.Nullable<System.Int32>..ctor
IL_0035:  stloc.s     05
IL_0037:  ldloca.s    05
IL_0039:  constrained. System.Nullable<System.Int32>
IL_003F:  callvirt    System.Object.ToString
IL_0044:  stloc.3     // result2
IL_0045:  ret

SomeClass.get_SomeNumber:
IL_0000:  ldarg.0
IL_0001:  ldfld       UserQuery+SomeClass.<SomeNumber>k__BackingField
IL_0006:  ret

SomeClass.set_SomeNumber:
IL_0000:  ldarg.0
IL_0001:  ldarg.1
IL_0002:  stfld       UserQuery+SomeClass.<SomeNumber>k__BackingField
IL_0007:  ret
类程序
{
静态void Main(字符串[]参数)
{
SomeClass someClass1=默认值(SomeClass);
字符串结果1=someClass1?.SomeNumber.ToString();
SomeClass someClass2=默认值(SomeClass);
字符串result2=(someClass2?.SomeNumber).ToString();
}
}
公共类
{
公共int SomeNumber{get;set;}
}
IL_0000:没有
IL_0001:ldnull
IL_0002:stloc.0//someClass1
IL_0003:ldloc.0//someClass1
IL_0004:brtrue.s IL_0009
IL_0006:ldnull
IL_0007:br.s IL_0018
IL_0009:ldloc.0//someClass1
IL_000A:调用UserQuery+SomeClass.get_SomeNumber
IL_000F:stloc.s 04
IL_0011:ldloca.s 04
IL_0013:调用System.Int32.ToString
IL_0018:stloc.1//result1
IL_0019:ldnull
IL_001A:stloc.2//someClass2
IL_001B:ldloc.2//someClass2
IL_001C:brtrue.s IL_002A
IL_001E:ldloca.s 05
IL_0020:initobj系统。可为空
IL_0026:ldloc.s 05
IL_0028:br.s IL_0035
IL_002A:ldloc.2//someClass2
IL\u 002B:调用UserQuery+SomeClass.get\u SomeNumber
IL_0030:newobj System.Nullable..ctor
IL_0035:stloc.s 05
IL_0037:ldloca.s 05
IL_0039:受限。系统。可为空
IL_003F:callvirt System.Object.ToString
IL_0044:stloc.3//result2
IL_0045:ret
SomeClass.get_SomeNumber:
IL_0000:ldarg.0
IL_0001:ldfld UserQuery+SomeClass.k_ubackingfield
IL_0006:ret
SomeClass.set\u SomeNumber:
IL_0000:ldarg.0
IL_0001:ldarg.1
IL\u 0002:stfld UserQuery+SomeClass.k\u BackingField
IL_0007:ret

使用
(someClass?.StatusCode)?.ToString()可以得到相同的结果这比Daniel A.White的回答更准确,它
短路
为空。请检查此项。我认为他们感到困惑的原因是他们不知道为什么评估会提前停止。因为他们不理解空条件运算符是如何工作的。因此,使用提供的代码示例进行解释。。我希望你的代码能说明发生了什么。它没有解释为什么或者如何知道发生了什么。我想我必须看看IL,因为你上面说的内容基本上是(null)。ToString的计算结果是一个空字符串,而它不是。唯一有意义的方法是,如果偏执论中的类型本质上是一个可为空的类型,就像上面Daniel White所说的。