32

Visual Studio 2010 を使用してアプリケーションを実行しているときに同僚が発生した例外を調査しています。

System.NullReferenceException was unhandled by user code
  Message=Object reference not set to an instance of an object.
  Source=mscorlib
  StackTrace:
       at System.Collections.Generic.GenericEqualityComparer`1.Equals(T x, T y)
       at System.Collections.Concurrent.ConcurrentDictionary`2.TryGetValue(TKey key, TValue& value)
       at xxxxxxx.xxxxxxx.xxxxxxx.RepositoryBase`2.GetFromCache(TIdentity id) 

.NET Reflectorを使用して のコードを調べましたが
GenericEqualityComparer<T>.Equals(T x, T y)NullReferenceException.

//GenericEqualityComparer<T>.Equals(T x, T y) from mscorlib 4.0.30319.269
public override bool Equals(T x, T y)
{
    if (x != null)
    {
        return ((y != null) && x.Equals(y));
    }
    if (y != null)
    {
        return false;
    }
    return true;
}

このスタック トレースT, TKeyでは、との型TIdentityはすべて同じ型です。

Identityこの型は、を実装すると呼ばれるカスタム型IEquatable<Identity>です。これは不変であり、 の実装で使用するフィールドに null 値を使用して構築することはできませんEquals(Identity other)。また、Equals(object obj)次のようにオーバーライドします。

public override bool Equals(object obj)
{
    if ((object)this == obj)
    {
        return true;
    }
    return Equals(obj as Identity);
}

public bool Equals(Identity other)
{
    if ((object)this == (object)other)
    {
        return true;
    }
    if ((object)other == null)
    {
        return false;
    }
    if (!FieldA.Equals(other.FieldA))
    {
        return false;
    }
    return FieldB.Equals(other.FieldB);
}

私は、実装に関するかなり網羅的な単体テストのセットを持っていますEquals。そのため、other/obj に対して null の値を喜んで受け入れ、期待どおりに false を返します。

==この型は、演算子も演算子もオーバーライドしません!=

それでも、私のクラスの の実装から例外がスローされた場合、スタック トレースの一番上に私のクラスが表示されることを期待しEquals(Identity other)ますIdentityNullReferenceExceptionmscorlib.

.NET Framework バージョン 4.0.30319.269 で実行しています。

私はメモリ ダンプを持っていません。これは以前に見たことがなく、それ以来再現していません。それでも、私は調査し、それが私たちのコードによって引き起こされたものではなく、本番環境では発生しないことを完全に確信する義務があります.

したがって、本当の問題は次のとおりです。この例外の原因は何ですか?

  • mscorlib のバグ (ほとんどありそうにない)
  • マシンの一時的なメモリ破損 (可能性あり、証拠でバックアップするのは難しい)
  • 他の?

* Jordão に対応する更新 *

Identity ではないオブジェクトでメソッドを呼び出すことはできますか?

は、 =および何もサブクラス化されないConcurrentDictionary<TKey, TValue>ように型付けされます。だから、どうやってそれが可能になるのかわかりません。TKeyIdentityIdentity

nullでメソッドを呼び出すことは可能ですか?

単体テストは、すべてのEquals実装を null で呼び出すシナリオをカバーしています。

スタック トレースはどのバージョンのコードからのものですか? 例外の影響を受けやすい古いバージョンでしょうか?

例外を生成したのと同じコードを分析しています。同僚のコンピューターで実行されている .NET Framework のバージョンも 4.0.30319.269 であることを確認しました。

マルチスレッドのシナリオで例外が発生する可能性がありますか? これらは通常、再現が困難ですが、調査する価値があるかもしれません。

はい、コードはマルチスレッドであり、意図されています。だから、それが私が使用している理由ですConcurrentDictionary

* Jalal Aldeen Saa'd からの応答に関連するフォローアップ *

パラメータが「ref」キーワードを使用して参照によって渡された場合にのみ、他のスレッドが設定する競合状態が原因であるxと考えていました。私は次のコードでその理論を検証することに着手しました:nullx

ManualResetEvent TestForNull = new ManualResetEvent(false);
ManualResetEvent SetToNull = new ManualResetEvent(false);

[TestMethod]
public void Test()
{
    var x = new object();
    var y = new object();

    var t = Task.Factory.StartNew(() =>
    {
        return Equals(x, y);
    });
    TestForNull.WaitOne(); //wait until x has been tested for null value
    x = null;
    SetToNull.Set(); //signal that x has now been set to null
    var result = t.Result;
    Assert.IsFalse(result);
}

public bool Equals<T>(T x, T y)
{
    if (x != null)
    {
        TestForNull.Set(); //signal that we have determined that x was not null
        SetToNull.WaitOne(); //wait for original x value to be set to null
        //would fail here if setting the outer scope x to null affected
        //the value of x in this scope
        return ((y != null) && x.Equals(y)); 
    }
    if (y != null)
    {
        return false;
    }
    return true;
}

テストはエラーなしで完了します。

署名を参照渡しx(yつまり、public bool Equals<T>(ref T x, ref T y) then the test fails with aNullReferenceException , but this does not match the method signature ofGenericEqualityComparer.Equals(T x, T y)`.

4

2 に答える 2

4

ここで私の仮説を立てます。

スタックを見ると、クラッシュが発生したのはここであると思われますが、クラッシュは別の場所で発生しています。私たちは間違ったスレッドを見ています。

これが実用的かどうかはわかりませんが、古き良き「printfデバッグ」が役立つ場合があります。を呼び出す前に、探している値を出力するとどうなりますTryGetValueか? ヌルを打ったかどうかがわかります。

于 2012-10-25T11:49:00.070 に答える
1

約2年前にEqualsでnull参照例外が発生しました(3.5または4.0であったかどうか、または修正されたかどうかはわかりません)。あなたのケースでどのタイプが比較されているかは私にはわかりませんが、私の状況では、ジェネリックメソッド宣言のMethodInfoリフレクションオブジェクトを非MethodInfoオブジェクトと比較するたびに発生します... Ka-boom!したがって、反射オブジェクトを比較している場合、これはそれである可能性があります。そうでない場合は、少なくとも、特定の状況で正当な理由なしにnull参照例外をスローできるEquals実装がBCLに少なくとも1つあることを証明できます。したがって、他の実装もある可能性があります。神聖な.NETBCLでさえソフトウェアであり、すべてのソフトウェアにバグがあります。

于 2012-10-31T00:10:03.600 に答える