不可能的NullReferenceException?

Eam*_*mon 32 c# mscorlib nullreferenceexception

我正在研究一位同事在通过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) 
Run Code Online (Sandbox Code Playgroud)

使用.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;
}
Run Code Online (Sandbox Code Playgroud)

堆栈跟踪中的类型T, TKey和类型TIdentity都相同.

该类型是一个称为Identity实现的自定义类型IEquatable<Identity>.它是不可变的,不能用它在其实现中使用的字段的空值构造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);
}
Run Code Online (Sandbox Code Playgroud)

我对这些Equals实现有一套相当详尽的单元测试.因此,它会愉快地接受其他/ 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*的回应有关的跟进

我本来以为,其中一些其他线程设置的竞争条件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;
}
Run Code Online (Sandbox Code Playgroud)

并且测试完成且没有错误.

我可以强制行为,如果我更改签名来传递xy通过引用(即,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 ofGenericEqualityComparer.Equals(T X,T Y)`.

MPe*_*ier 4

我将在这里阐述我的假设。

堆栈让您相信这是崩溃发生的地方,但它发生在其他地方。我们正在寻找错误的线索。

我不知道这是否实用,但有时好的旧“printf 调试”会有所帮助。如果您在调用之前打印出您要查找的值怎么办TryGetValue?你会看到你是否击中了空值。

  • 是的,这在原来的问题中已经说过了。这是我之前和之后都没有见过的一次性错误,因此需要使用异常中给出的堆栈跟踪通过代码的静态分析进行调试。因此,我认为由于某种超出我控制范围的暂时性问题(内存损坏等)而将其归类为异常可能是公平的,并继续处理更重要的事情。 (2认同)