簡體   English   中英

函數返回時為NullReferenceException

[英]NullReferenceException when function returns

運行多線程應用程序時,但僅當我在調試器外部以發布模式運行時,才得到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”不為null,並且我也嘗試將foreach更改為for,以防IEnumerator做一些有趣的事情,但是異常發生在同一行。

關於如何進一步調查的任何想法?

更新:一些響應者提出了與確保記錄器不為空有關的可能解決方案。 為了清楚起見,在異常開始發生之后,添加了日志記錄語句以用於調試。

我找到了空引用。 就像Fredrik和micahtan所建議的那樣,我沒有為社區提供足夠的信息來找到解決方案,所以我認為我應該發布我發現的內容以解決這個問題。

這表示正在發生的事情:

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”變量作為臨時強引用,直到Update方法獲得永久引用。 在發布模式下,編譯器優化了“ something = m_object.Add();”。 分配,因為直到重新分配“東西”才使用。 這允許將ISomething進行垃圾回收,因此當我嘗試訪問它時,它不再存在於m_collection中。

我要做的就是確保在致電Update之前,我擁有一份強有力的參考。

我懷疑這對任何人都沒有用,但是如果有人好奇,我不想讓這個問題沒有答案。

它記錄為“十”的事實使我首先關注:

  • 曾經分配過logger嗎...這也許會以某種方式變為空值
  • Log本身內部的錯誤

沒有足夠的背景信息就很難說出來-但這就是我要調查的方式。 您還可以在某個地方添加一個簡單的null測試; 作為一種輕松的方法,您可以將Log方法重命名為其他方法,然后添加擴展方法:

[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);
       }
    }
}

您現有的代碼應調用新的Log擴展方法,並且該異常將使其清楚地明確地划入位置。 修復后,也許可以將其改回...或者保留它。

您是否正在從多個線程修改classItemCollection? 如果在另一個線程中更改集合,則可能會使迭代器無效,這可能導致異常。 您可能需要用鎖保護訪問。

編輯:您可以發布有關ClassItem和classItemCollection類型的更多信息嗎?

另一種可能性是ClassItem是一個值類型,而classItemCollection是一個通用集合,並且以某種方式將null添加到該集合中。 以下引發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解決嗎? foreach中的i或Object i或使用通用容器。

同意弗雷德里克(Fredrik)-需要更多詳細信息。 也許一個地方可以開始尋找:您提到了多線程應用程序以及發行版中發生的錯誤,但沒有調試。 您可能會遇到多個線程訪問同一對象引用的計時問題。

無論如何,我可能還會提出:

Debug.Assert(classItemCollection != null);

就在循環迭代之前。 它在發布模式下無濟於事,但如果(何時?)在Debug中發生,則可能會幫助您解決問題。

我會尋找將logger或其依賴項之一設置為null的代碼。 記錄器的屬性是否設置為null可能會觸發此事件? 釋放模式有時會加快應用程序的執行速度,這可能會揭示同步問題,這些問題被調試模式和/或調試器的性能損失所掩蓋。

“ 11”沒有得到記錄的事實使我相信記錄器將在調用之前設置為null。 您可以將其包裝在try / catch中,看看它是否碰到了塊的catch部分? 也許您可以插入MessageBox.Show或在發生這種情況時將某些內容寫入已知文件。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM