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)
ますIdentity
がNullReferenceException
、mscorlib
.
.NET Framework バージョン 4.0.30319.269 で実行しています。
私はメモリ ダンプを持っていません。これは以前に見たことがなく、それ以来再現していません。それでも、私は調査し、それが私たちのコードによって引き起こされたものではなく、本番環境では発生しないことを完全に確信する義務があります.
したがって、本当の問題は次のとおりです。この例外の原因は何ですか?
- mscorlib のバグ (ほとんどありそうにない)
- マシンの一時的なメモリ破損 (可能性あり、証拠でバックアップするのは難しい)
- 他の?
* Jordão に対応する更新 *
Identity ではないオブジェクトでメソッドを呼び出すことはできますか?
は、 =および何もサブクラス化されないConcurrentDictionary<TKey, TValue>
ように型付けされます。だから、どうやってそれが可能になるのかわかりません。TKey
Identity
Identity
nullでメソッドを呼び出すことは可能ですか?
単体テストは、すべてのEquals
実装を null で呼び出すシナリオをカバーしています。
スタック トレースはどのバージョンのコードからのものですか? 例外の影響を受けやすい古いバージョンでしょうか?
例外を生成したのと同じコードを分析しています。同僚のコンピューターで実行されている .NET Framework のバージョンも 4.0.30319.269 であることを確認しました。
マルチスレッドのシナリオで例外が発生する可能性がありますか? これらは通常、再現が困難ですが、調査する価値があるかもしれません。
はい、コードはマルチスレッドであり、意図されています。だから、それが私が使用している理由ですConcurrentDictionary
。
* Jalal Aldeen Saa'd からの応答に関連するフォローアップ *
パラメータが「ref」キーワードを使用して参照によって渡された場合にのみ、他のスレッドが設定する競合状態が原因であるx
と考えていました。私は次のコードでその理論を検証することに着手しました:null
x
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 a
NullReferenceException , but this does not match the method signature of
GenericEqualityComparer.Equals(T x, T y)`.