为什么在这种情况下不空合并运算符(?)工作? [英] Why doesn't the null coalescing operator (??) work in this situation?

查看:146
本文介绍了为什么在这种情况下不空合并运算符(?)工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我却得到了意想不到的NullReferenceException 当我运行这段代码,省略了 fileSystemHelper 参数(并因此拖欠它是null):​​

I'm getting an unexpected NullReferenceException when I run this code, omitting the fileSystemHelper parameter (and therefore defaulting it to null):

public class GitLog
    {
    FileSystemHelper fileSystem;

    /// <summary>
    ///   Initializes a new instance of the <see cref="GitLog" /> class.
    /// </summary>
    /// <param name="pathToWorkingCopy">The path to a Git working copy.</param>
    /// <param name="fileSystemHelper">A helper class that provides file system services (optional).</param>
    /// <exception cref="ArgumentException">Thrown if the path is invalid.</exception>
    /// <exception cref="InvalidOperationException">Thrown if there is no Git repository at the specified path.</exception>
    public GitLog(string pathToWorkingCopy, FileSystemHelper fileSystemHelper = null)
        {
        this.fileSystem = fileSystemHelper ?? new FileSystemHelper();
        string fullPath = fileSystem.GetFullPath(pathToWorkingCopy); // ArgumentException if path invalid.
        if (!fileSystem.DirectoryExists(fullPath))
            throw new ArgumentException("The specified working copy directory does not exist.");
        GitWorkingCopyPath = pathToWorkingCopy;
        string git = fileSystem.PathCombine(fullPath, ".git");
        if (!fileSystem.DirectoryExists(git))
            {
            throw new InvalidOperationException(
                "There does not appear to be a Git repository at the specified location.");
            }
        }

当我单步在调试器中的代码,后我跨过第一行(以 ?? 运营商),那么文件系统仍具有null值,如图该屏幕剪断(步过下一行抛出的NullReferenceException ):
当为空不空?

When I single step the code in the debugger, after I step over the first line (with the ?? operator) then fileSystem still has the value null, as shown in this screen snip (stepping over the next line throws NullReferenceException):

这是不是我所期待的!我期待的空合并运算符,以发现该参数为空,并创建一个新FileSystemHelper()。我在此代码为年龄怔怔地看不出有什么不妥的地方。

This is not what I expected! I'm expecting the null coalescing operator to spot that the parameter is null and create a new FileSystemHelper(). I have stared at this code for ages and can't see what is wrong with it.

ReSharper的人士指出,该领域在这一个方法只使用,所以可以可能被转换为一个局部变量...所以我想,你猜怎么着?有效。所以,我有我的修正,但我不能为我的生活为什么上面的代码不起作用。我觉得我是在学习C#一些有趣的边缘,要么或我已经做了一些非常愚蠢的。 ?任何人都可以看到这里发生了什么。

ReSharper pointed out that the field is only used in this one method, so could potentially be converted to a local variable... so I tried that and guess what? It worked. So, I have my fix, but I cannot for the life of me see why the code above doesn't work. I feel like I am on the edge of learning something interesting about C#, either that or I've done something really dumb. Can anyone see what's happening here?

推荐答案

我用下面的代码复制它在VS2012:

I have reproduced it in VS2012 with the following code:

public void Test()
{
    TestFoo();
}

private Foo _foo;

private void TestFoo(Foo foo = null)
{
    _foo = foo ?? new Foo();
}

public class Foo
{
}

如果您在命名为testFoo 方法的末尾设置一个断点,你希望看到的 _foo 变量集,但它仍然会显示在调试器为空。

If you set a breakpoint at the end of the TestFoo method, you would expect to see the _foo variable set, but it will still show as null in the debugger.

但是,如果你再这样做的任何的有 _foo ,它会显示正确。即使是一个简单的任务,如

But if you then do anything with _foo, it then appears correctly. Even a simple assignment such as

_foo = foo ?? new Foo();
var f = _foo;

如果您通过它一步,你会看到 _foo 显示空,直到它被分配到˚F

If you step through it, you'll see that _foo shows null until it is assigned to f.

这让我想起了延迟执行的行为,例如与LINQ,但我找不到任何可以确认这一点。

This reminds me of deferred execution behavior, such as with LINQ, but I can't find anything that would confirm that.

这是完全可能的,这只是一个调试器的怪癖。也许有人MSIL技能可以摆脱对什么是引擎盖下发生的一些轻

It's entirely possible that this is just a quirk of the debugger. Perhaps someone with MSIL skills can shed some light on what is happening under the hood.

同样有趣的是,如果你更换空合并运算符与它的等效的:

Also interesting is that if you replace the null coalescing operator with it's equivalent:

_foo = foo != null ? foo : new Foo();



然后,它不会出现此行为。

Then it does not exhibit this behavior.

我不是一个装配/ MSIL的家伙,但只是走一下两个版本之间的dissasembly输出有趣的是:

I am not an assembly/MSIL guy, but just taking a look at the dissasembly output between the two versions is interesting:

        _foo = foo ?? new Foo();
0000002d  mov         rax,qword ptr [rsp+68h] 
00000032  mov         qword ptr [rsp+28h],rax 
00000037  mov         rax,qword ptr [rsp+60h] 
0000003c  mov         qword ptr [rsp+30h],rax 
00000041  cmp         qword ptr [rsp+68h],0 
00000047  jne         0000000000000078 
00000049  lea         rcx,[FFFE23B8h] 
00000050  call        000000005F2E8220 
        var f = _foo;
00000055  mov         qword ptr [rsp+38h],rax 
0000005a  mov         rax,qword ptr [rsp+38h] 
0000005f  mov         qword ptr [rsp+40h],rax 
00000064  mov         rcx,qword ptr [rsp+40h] 
00000069  call        FFFFFFFFFFFCA000 
0000006e  mov         r11,qword ptr [rsp+40h] 
00000073  mov         qword ptr [rsp+28h],r11 
00000078  mov         rcx,qword ptr [rsp+30h] 
0000007d  add         rcx,8 
00000081  mov         rdx,qword ptr [rsp+28h] 
00000086  call        000000005F2E72A0 
0000008b  mov         rax,qword ptr [rsp+60h] 
00000090  mov         rax,qword ptr [rax+8] 
00000094  mov         qword ptr [rsp+20h],rax 

相比之下,内联,如果版本:

Compare that to the inlined-if version:

        _foo = foo != null ? foo : new Foo();
0000002d  mov         rax,qword ptr [rsp+50h] 
00000032  mov         qword ptr [rsp+28h],rax 
00000037  cmp         qword ptr [rsp+58h],0 
0000003d  jne         0000000000000066 
0000003f  lea         rcx,[FFFE23B8h] 
00000046  call        000000005F2E8220 
0000004b  mov         qword ptr [rsp+30h],rax 
00000050  mov         rax,qword ptr [rsp+30h] 
00000055  mov         qword ptr [rsp+38h],rax 
0000005a  mov         rcx,qword ptr [rsp+38h] 
0000005f  call        FFFFFFFFFFFCA000 
00000064  jmp         0000000000000070 
00000066  mov         rax,qword ptr [rsp+58h] 
0000006b  mov         qword ptr [rsp+38h],rax 
00000070  nop 
00000071  mov         rcx,qword ptr [rsp+28h] 
00000076  add         rcx,8 
0000007a  mov         rdx,qword ptr [rsp+38h] 
0000007f  call        000000005F2E72A0 
        var f = _foo;
00000084  mov         rax,qword ptr [rsp+50h] 
00000089  mov         rax,qword ptr [rax+8] 
0000008d  mov         qword ptr [rsp+20h],rax 

,我不觉得有某种延迟执行发生的事情。在第二示例中的赋值语句是非常小的相比于第一示例

Based on this, I do think there is some kind of deferred execution happening. The assignment statement in the second example is very small in comparison to the first example.

这篇关于为什么在这种情况下不空合并运算符(?)工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆