2016-03-10 41 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 –

相关问题