2016-03-10 71 views
0

所以在我XXX.OnPropertyChanged()方法,我有:爲什麼我有時會在BeginInvoke塊中得到NullReferenceException?

public class XXX : IProperyNotifyChanged { 
    Control itsCtrl; 
    ... 

    public void Init(Control ctrl) { 
     itsCtrl = ctrl; 
    } 

    public void OnPropertyChanged(string propertyName) { 
    if (PropertyChanged != null) { 
     if (itsCtrl.InvokeRequired) { 
      itsCtrl.BeginInvoke(() => { 
       PropertyChanged(this, propertyName); 
      }); 
     } else { 
      PropertyChanged(this, propertyName); 
     } 
     } 
    } 
} 

我認爲,這將引發以下異常(很少,但現在更經常發生):

System.Reflection.TargetInvocationException was unhandled 
    HResult=-2146232828 
    Message=Exception has been thrown by the target of an invocation. 
    Source=mscorlib 
    StackTrace: 
     at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) 
     at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) 
     at System.Delegate.DynamicInvokeImpl(Object[] args) 
     at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme) 
     at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj) 
     at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
     at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme) 
     at System.Windows.Forms.Control.InvokeMarshaledCallbacks() 
     at System.Windows.Forms.Control.WndProc(Message& m) 
     at System.Windows.Forms.Form.WndProc(Message& m) 
     at DevExpress.XtraEditors.XtraForm.WndProc(Message& msg) 
     at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) 
     at DevExpress.Utils.Win.Hook.ControlWndHook.CallWindowProc(IntPtr pPrevProc, IntPtr hWnd, Int32 message, IntPtr wParam, IntPtr lParam) 
     at DevExpress.Utils.Win.Hook.ControlWndHook.WindowProc(IntPtr hWnd, Int32 message, IntPtr wParam, IntPtr lParam) 
     at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg) 
     at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData) 
     at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context) 
     at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context) 
     at Client.Program.Main() in C:\Client\Program.cs:line 18 
     at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
     at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
     at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
     at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
     at System.Threading.ThreadHelper.ThreadStart() 
    InnerException: 
     HResult=-2147467261 
     Message=Object reference not set to an instance of an object. 
     Source=XXX 
     StackTrace: 
      at XXX.<>c__DisplayClass442_0.<OnPropertyChanged>b__0() 
     InnerException: 

我只是在想。發生這種情況是因爲在調用BeginInvoke之前,我沒有像thispropertyName那樣正確複製變量?或者是別的什麼?這種情況很少發生,我不知道如何重現它,而且我無法從堆棧跟蹤中獲得太多東西。你會如何解決這個問題?

+0

是 「itsCtrl」 永遠空?在開始調用之前添加一個空檢查,看看問題是否消失 – bgura

+0

itsCtrl是MainForm。它永遠不可能是空的。 – Denis

+0

@Denis這是不正確的。當表單加載時,它可以是'null'。您遇到競爭狀況。 –

回答

2

我只是在想。發生這種情況是因爲在調用BeginInvoke之前,我沒有正確複製像this和propertyName這樣的變量?

this總是固有地在堆棧上,並且不能被分配給別的東西,所以它不能在方法中設置爲null。 propertyName是本地的,所以在那裏不會有比賽。

PropertyChanged雖然不是本地的,但每次都獲得。當你這樣做:

if (PropertyChanged != null) 
{ 
    PropertyChanged.BeginInvoke(…); 
} 

它就像:

PropertyChangedEventHandler local1 = PropertyChanged; // Get value from property; 
if (local1 != null) 
{ 
    PropertyChangedEventHandler local2 = PropertyChanged; // Get value from property; 
    local2.BeginInvoke(…); 
} 

有一個爲PropertyChanged在此期間被設置爲空的機會。 你想的副本內容:

var propChanged = PropertyChanged; 
if (propChanged != null) 
{ 
    propChanged.BeginInvoke(…); 
} 

現在要麼propChanged將是空的整個方法的持續時間,或者也不會是,競賽已經一去不復返了。

確實:

PropertyChanged?.BeginInvoke(…); 
+0

如果我做PropertyChanged.BeginInvoke()。 PropChanged是一個事件。執行什麼線程?假設我從非GUI線程調用它... – Denis

+0

如果你有這樣的風險,那麼在那裏重做空檢查(使用空檢查語法,其他答案提到我懷疑在那裏確實會很好) –

1

我強烈建議利用的空條件運算符的所帶來的C#6.0,如果可以的話:

itsCtrl.InvokeRequired(...) should be  itsCtrl?.InvokeRequired(...) 
itsCtrl.BeginInvoke(...)  should be  itsCtrl?.BeginInvoke(...) 

不像你所相信的,而形式加載,你的控制可能是null,和所以你從比賽條件中獲得了例外。

你應該做同樣的你的PropertyChanged調用:

PropertyChanged(...) should be PropertyChanged?.Invoke(...) 

這是線程安全的,將避免在您的支票if (PropertyChanged != null)不再是真實的情況,由於一些其他線程改變它。

+0

'PropertyChanged ?.調用(...)'會很好,如果唯一取決於它不是null的是否調用'Invoke'調用,但這不是, –

+0

@JonHanna我沒有關注你,你能否詳細說明?空條件是線程安全的,與將'PropertyChanged'分配給局部變量的答案完全相同。 –

+0

但本地只持續那一個電話。雖然等同於我的答案中的縮寫形式,但它不等同於問題,因爲在if語句塊中有更多內容比單個調用BeginInvoke –

相關問題