代码之家  ›  专栏  ›  技术社区  ›  Jeff Hillman

函数返回时发生NullReferenceException

  •  1
  • Jeff Hillman  · 技术社区  · 17 年前

    当运行我的多线程应用程序时,我会得到一个nullreferenceexception,但仅当我在调试器之外的发布模式下运行时。堆栈跟踪会被记录,并且它总是指向同一个函数调用。我在函数中放置了几条日志记录语句,以尝试确定它会达到什么程度,并且每个语句都会被记录,包括函数最后一行的一条语句。有趣的是,当发生NullReferenceException时,函数调用后的语句不会被记录:

        // ...
        logger.Log( "one" );  // logged
        Update( false );
        logger.Log( "eleven" );  // not logged when exception occurs
    }
    
    private void Update( bool condition )
    {
        logger.Log( "one" );  // logged
        // ...  
        logger.Log( "ten" );  // logged, even when exception occurs
    }
    

    每次调用函数时不会发生异常。在函数执行之前或执行过程中,堆栈是否可能已损坏,从而导致返回地址丢失,从而导致空引用?我不认为在.NET下这种事情是可能的,但我想奇怪的事情已经发生了。

    我尝试用函数的内容替换对函数的调用,所以所有的事情都是内联的,然后异常发生在一行,如下所示:

    foreach ( ClassItem item in classItemCollection )
    

    我已经通过日志验证了“ClassItemCollection”不是空的,并且我还尝试将foreach更改为for,以防IEnumerator做了一些有趣的事情,但异常发生在同一行。

    关于如何进一步调查这个问题有什么想法吗?

    更新: 一些响应者建议了可能的解决方案,这些解决方案必须确保记录器不为空。为了清楚起见,在异常开始发生之后,为了调试目的添加了日志语句。

    6 回复  |  直到 17 年前
        1
  •  3
  •   Jeff Hillman    17 年前

    我找到了我的空引用。就像弗雷德里克和米卡坦建议的那样,我没有为社区提供足够的信息来找到解决方案,所以我想我应该把我发现的发布出来,只是为了让它休息一下。

    这是对正在发生的事情的描述:

    ISomething something = null;
    
    //...
    
    // the Add method returns a strong reference to an ISomething
    // that it creates.  m_object holds a weak reference, so when
    // "this" no longer has a strong reference, the ISomething can
    // be garbage collected.
    something = m_object.Add( index );
    
    // the Update method looks at the ISomethings held by m_object.
    // it obtains strong references to any that have been added,
    // and puts them in m_collection;
    Update( false );
    
    // m_collection should hold the strong reference created by 
    // the Update method.
    // the null reference exception occurred here
    something = m_collection[ index ];
    
    return something;
    

    问题是我使用了“something”变量作为临时的强引用,直到更新方法获得永久引用。在发布模式下,编译器会优化掉“something=m_object.add();”赋值,因为“something”在再次赋值之前不会被使用。这允许对isometing进行垃圾收集,因此当我尝试访问它时,它不再存在于m_集合中。

    我所要做的就是确保在电话更新之前我一直持有一份强有力的推荐信。

    我怀疑这对任何人都有用,但如果有人好奇,我不想让这个问题没有答案。

        2
  •  2
  •   Marc Gravell    17 年前

    它记录了“十”这个事实,这让我首先想到:

    • 是 logger 曾经分配过…这可能会变得无效吗?
    • 虫子在里面吗 Log 它本身

    如果没有足够的上下文,很难说出来——但这就是我将如何调查的。您还可以添加一个简单的 null 在某个地方测试;作为一种厚颜无耻的方法,您可以重命名 原木 方法,并添加扩展方法:

    [Conditional("TRACE")]
    public static void Log(this YourLoggerType logger, string message) {
        if(logger==null) {
           throw new ArgumentNullException("logger",
                "logger was null, logging " + message);
        } else {
           try {
               logger.LogCore(message); // the old method
           } catch (Exception ex) {
               throw new InvalidOperationException(
                    "logger failed, logging " + message, ex);
           }
        }
    }
    

    您现有的代码应该调用新的 原木 扩展方法和异常将使它清楚地显示出它的错误。也许一修好就换回去…或者离开它。

        3
  •  0
  •   micahtan    17 年前

    同意W/Fredrik——需要更多细节。一个可能开始寻找的地方:您提到了多线程应用程序以及在发行版中发生的错误,但没有提到调试。您可能遇到一个时间问题,多个线程访问同一对象引用。

    不管怎样,我可能还会放一个:

    Debug.Assert(classItemCollection != null);
    

    就在循环迭代之前。在释放模式下它不会帮助您,但如果(什么时候?),它可能会帮助您解决问题。它发生在调试中。

        4
  •  0
  •   Shea    17 年前

    我将查找将logger或其依赖项之一设置为空的代码。记录器的属性在设置为空时是否可能触发此事件?发布模式有时会加快应用程序的执行速度,从而揭示被调试模式和/或调试器的性能惩罚掩盖的同步问题。

        5
  •  0
  •   jasonh    17 年前

    事实上,“11”没有被记录,这让我相信在调用之前,记录器被设置为空。你能把它包装在一个try/catch中看看它是否碰到了块的catch部分吗?也许您可以插入一个消息框。当发生这种情况时,显示或写入某个已知文件。

        6
  •  0
  •   Dolphin    17 年前

    是否从多个线程修改ClassItemCollection?如果在另一个线程中更改集合,则可能会使迭代器无效,这可能会导致异常。您可能需要用锁保护访问。

    编辑: 你能发布更多关于ClassItem和ClassItemCollection类型的信息吗?

    另一种可能性是ClassItem是值类型,ClassItemCollection是通用集合,并且不知何故会向集合中添加空值。以下引发NullReferenceException:

            ArrayList list=new ArrayList();
    
            list.Add(1);
            list.Add(2);
            list.Add(null);
            list.Add(4);
    
            foreach (int i in list)
            {
                System.Diagnostics.Debug.WriteLine(i);
            }
    

    这个特殊的问题可以通过int来解决?我或对象i在foreach中或使用通用容器。

    推荐文章