为什么在这种情况下不空合并运算符(?)工作? [英] Why doesn't the null coalescing operator (??) work in this situation?
问题描述
我却得到了意想不到的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屋!