2013-10-14 13 views
15

私はこのコードを実行すると、私はfileSystemHelperパラメータを省略(したがって、nullにそれを不履行)、予期しないNullReferenceExceptionを取得しています:この状況で、ヌル合体演算子(??)が機能しないのはなぜですか?

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."); 
      } 
     } 

私は上のステップの後、デバッガでコードをステップ選抜するときこの画面のスニップに示すように(??オペレータとの)最初の行は、その後fileSystemまだ値nullを持ち、(次の行をステップオーバースローNullReferenceException): When is null not null?

これは私が期待したものではありません! null合体演算子がnullであることを確認し、new FileSystemHelper()を作成することを期待しています。私は年齢のためにこのコードを見て、何が間違っているか見ることができません。

ReSharperは、フィールドはこの1つのメソッドでのみ使用されると指摘しているので、ローカル変数に変換される可能性があります。出来た。だから、私は修正がありますが、私の人生では上記のコードがうまくいかない理由がわかりません。私はC#について興味深いことを学ぶことの端にいるような気がします。それは本当に何かをやったことです。誰がここで何が起こっているのを見ることができますか?

+0

すでに 'fileSystemHelper'がメソッドのパラメータに' null'と指定されていますが、わかりませんが、それは何か関係があります。しかし、再び、私は推測しています。 – Tico

+2

NREが* GetFullPath内から*発生していないことを確かめていますか?私は上記のコードでは何も言わないと言います。 – user2864740

+0

OK、VisualStudioを終了して何か他のことをしてからリロードしたが、今はすべて動作しているので問題を再現できない。私はこれがReSharperユニットテストランナーの奇妙なキャッシングの問題かもしれません。私は単純なMSpecテストを使ってコードを実行しています。 ReSharperは単体テストを実行しているときにアセンブリのシャドウコピーをとります。時には時にはシャドウコピーが「固まった」ように見えることもあります。だから、おそらく、私は手動ですべてを再構築していたとしても、実際には古いコードを実行していました。それは私が持っている最良の説明です... –

答えて

12

:あなたはTestFooメソッドの最後にブレークポイントを設定した場合、あなたは_foo変数セットを見ることを期待するが、それはまだ表示されます

public void Test() 
{ 
    TestFoo(); 
} 

private Foo _foo; 

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

public class Foo 
{ 
} 

デバッガではnullとして返されます。

_fooを入力すると、正しく表示されます。あなたはそれをステップ実行した場合であっても、単純な割り当ては、このような

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

として、あなたはそれがfに割り当てられるまで_fooがnull示していることがわかります。

これは、LINQなどの遅延実行動作を思い出させますが、これを確認するものは見つかりません。

これはデバッガの単なるクォークに過ぎません。おそらく、MSILのスキルを持つ人は、ボンネットの下で何が起こっているのかを明らかにすることができます。

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

を次に、それは、この動作を示さない:

また興味深いのは、それが同等だとあなたはnull合体演算子を交換した場合ということです。

私は、アセンブリ/ MSILの男ではないけど、ちょうど2つのバージョン間dissasembly出力を見て取ることは興味深いです:

 _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 

 _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 

はインライン展開-場合のバージョンにすることを比較

これに基づいて、何らかの遅延実行が起こっていると思います。 2番目の例の代入文は、最初の例と比較して非常に小さいです。

+3

これは、64ビットデバッガの問題であるようです。ターゲットとする「任意のCPU」のビルドとコンパイルは、この動作を示します。 x86をターゲットに変更すると、もはや問題にはなりません。 –

+3

私は問題が何かを知っていると思う... 64ビットビルドでVSによって生成された行番号が間違っています。ヌル合体ラインに続くラインに対して生成されたライン番号は、実際にはヌル合体命令の「中間」にある。したがって、デバッガはその行でブレークしますが、前の命令はまだ完全に完了していません。その行を踏むとその命令が終了します。逆アセンブリをステップ実行すると、メンバ変数が設定されるポイントを見ることができます。 –

+0

@JeffMercado - 確認済み。これは「Any CPU」または「x64」でのみ実行されます。 "x86"でうまく動作します。 –

1

他の誰かがthis questionで同じ問題を経験しました。興味深いことに、this._field = expression ?? new ClassName();形式も使用しています。デバッガで何らかの問題が発生する可能性があります。値を書き出すことで正しい結果が得られたようです。

デバッグ/ログコードを追加して、割り当て後にフィールドの値を表示して、接続されているデバッガの奇妙さを排除してください。

私は次のコードでVS2012でそれを再現している
関連する問題