不可能的NullReferenceException?

时间:2012-10-25 00:52:37

标签: c# nullreferenceexception mscorlib

我正在研究一位同事在通过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;
}

stack traceT, TKeyTIdentity的类型都相同。

该类型是名为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);
}

我对Equals实现有一套相当详尽的单元测试。因此,它会愉快地接受其他/ obj的null值,并按预期返回false。

该类型不会覆盖==运算符或!=运算符。

即便如此,如果我的Equals(Identity other)Identity的实现中抛出了异常,我希望看到我的类位于堆栈跟踪之上,但它会显示{{1来自NullReferenceException

我在.NET Framework 4.0.30319.269上运行。

我没有内存转储,之前我没有看过这个,之后就再也没有了。不过,我不得不进行调查,并且绝对肯定它不是由我们的代码引起的,并且它不会在生产中发生。

所以,真正的问题是:是什么导致了这个例外?

  • mscorlib中的错误(似乎极不可能)
  • 机器上的瞬态内存损坏(可能,很难用证据备份)
  • 其他?

*响应Jordão*

的更新
  

是否可以使用非Identity的对象调用该方法?

键入mscorlibConcurrentDictionary<TKey, TValue> = TKey,没有子类Identity。所以,我看不出它是如何可能的。

  

是否可以使用null调用方法?

单元测试涵盖了使用null调用所有Identity实现的方案。

  

什么版本的代码是堆栈跟踪?也许某些旧版本容易受到例外的影响?

我正在分析生成异常的相同代码。我已经检查过在我的同事计算机上运行的.NET Framework版本也是4.0.30319.269。

  

任何多线程场景都可能导致异常?这些通常难以复制,但可能值得研究。

是的,代码是多线程的,并且打算这样做。所以,这就是我使用Equals

的原因

*与Jalal Aldeen Saa'd *的反应相关的跟进

我原本认为,如果使用'ref'关键字通过引用传递参数ConcurrentDictionary,其他线程设置xnull的竞争条件只能是原因。我打算用以下代码验证该理论:

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 NullReferenceException public bool Equals<T>(ref T x, ref T y) then the test fails with a GenericEqualityComparer.Equals(T x, T y)`。

2 个答案:

答案 0 :(得分:4)

我会在这里列出我的假设。

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

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

答案 1 :(得分:1)

几年前我在Equals中遇到了一个空引用异常(不确定它是在3.5还是4.0,或者它是否曾经修复过)。我不清楚在你的情况下比较了什么类型,但在我的情况下,只要比较一般方法声明的MethodInfo反射对象和任何非MethodInfo对象,就会发生这种情况...... Ka-boom!所以,如果你要比较反射对象,可能就是这样。如果你不是,至少我可以证明在BCL中至少有一个Equals实现可以在某些情况下无理由地抛出空引用异常,所以可能还有其他的。即使神圣的.NET BCL仍然是软件,所有软件都有漏洞。