Ste*_*hen 1 c# multithreading inotifypropertychanged
如何让您的对象保持线程安全,实现INotifyPropertyChanged?我不能使用SynchronizationContext,因为我需要能够序列化对象.
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
// What can I add here to make it thread-safe?
handler(this, new PropertyChangedEventArgs(propertyName));
}
Run Code Online (Sandbox Code Playgroud)
所以......如果您不介意在编译时依赖于为您生成一些代码的扩展,那么实际上这是一种非常好的方法.无论如何,我正在使用Fody/PropertyChanged,这是一个非常容易的变化.这避免了必须引用SynchronizationContext真正没有业务知道UI的模型.
首先,安装NuGet提供的PropertyChanged.Fody.
当前实现的每个类INofityPropertyChanged都应该具有该属性[ImplementPropertyChanged].应删除手动实施.有关更多信息,请参阅自述文件和维基.
一个PropertyChangedNotificationInterceptor需要实现.它们为wiki中的WPF提供了一个示例; 我对WinForms项目的实现:
public static class PropertyChangedNotificationInterceptor
{
public static SynchronizationContext UIContext { get; set; }
public static void Intercept(object target, Action onPropertyChangedAction, string propertyName)
{
if (UIContext != null)
{
UIContext.Post(_ =>
{
onPropertyChangedAction();
}, null);
}
else
{
onPropertyChangedAction();
}
}
}
Run Code Online (Sandbox Code Playgroud)设置在PropertyChangedNotificationInterceptor.UIContext某个地方.您可以放入PropertyChangedNotificationInterceptor.UIContext = WindowsFormsSynchronizationContext.Current;主窗体的构造函数.
该Form构造穿过控制构造,并最终结束了创建WindowsFormsSynchronizationContext,如果一个不存在.因此,捕获.Current这里是安全的.(注意:这可能是一个实现细节,可能会在将来的.NET版本,不同的平台等中更改)
请记住,只有在您只有一个同步上下文(单个UI线程)时,这才有效.如果您遇到多个UI线程的混乱,并且需要多个数据绑定,这将变得更加复杂.所以请不要这样做.
感谢Simon Cropp的写作PropertyChanged,并帮助我找到它的这个特殊功能.
如果您使用的是 WPF,则可以使用 Dispatcher 封送对 UI 线程的调用。
App.Current.Dispatcher.Invoke(new Action(()=>{
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}));
Run Code Online (Sandbox Code Playgroud)
小智 5
如果您不幸运并且必须使用 Winforms,请尝试使用应用程序的 MainForm 来调用 UI 线程中的处理程序。不好的是你必须包括
using System.Windows.Forms;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
if (Application.OpenForms.Count == 0) return;
var mainForm = Application.OpenForms[0];
if(mainForm == null) return; // No main form - no calls
if (mainForm.InvokeRequired)
{
// We are not in UI Thread now
mainform.Invoke(handler, new object[] {
this, new PropertyChangedEventArgs(propName)});
}
else
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Run Code Online (Sandbox Code Playgroud)