C# 延迟事件处理,直到触发事件
在C#中,在实体被完全修改之前,延迟处理所有已知事件的最佳方式是什么? 例如,假设一个实体MyEntity具有属性ID、名称和描述C# 延迟事件处理,直到触发事件,c#,events,event-handling,C#,Events,Event Handling,在C#中,在实体被完全修改之前,延迟处理所有已知事件的最佳方式是什么? 例如,假设一个实体MyEntity具有属性ID、名称和描述 public class MyEntity { public Int32 ID { get; set; } public String Name { get; set; } public String Description { get; set; } } 修改这些属性时,会为每次修改触发一个事件 有时,
public class MyEntity
{
public Int32 ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
}
修改这些属性时,会为每次修改触发一个事件
有时,ID是唯一修改的属性,有时修改所有属性。我希望修改事件的注册侦听器等待“批处理”中所有被修改的属性都被修改
实现这一目标的最佳方式是什么
在我看来,类似于UnitOfWork模式的东西,可以在调用堆栈的顶层围绕方法调用包装一个using语句,但不知道如何实现这样的事情
编辑:
作为澄清。。。侦听器分布在应用程序中,并在其他线程中执行。例如,另一个参与者设置它必须调用MyEntity.name属性才能设置值的名称
由于设计原因,对Name属性的修改可能会触发其他属性的更改,因此监听器需要知道属性的修改已经完成。只有执行修改的代码才能知道其一批更改何时完成 我对类似类所做的是提供
SuspendNotifications()
和resumennotifications()
方法,这些方法以明显的方式调用(即在进行一系列更改之前调用suspend,完成后调用resume)
它们在内部维护一个计数器,该计数器在SuspendNotifications()中递增,在ResumeNotifications()中递减,如果递减结果为零,则发出通知。我这样做是因为有时我会修改一些属性,然后调用另一个方法,该方法会修改更多属性,而该方法本身会调用suspend/resume
(如果调用resume的次数太多,我抛出了一个异常。)
如果更改了多个属性,则最终通知不会命名要更改的属性(因为有多个属性)。我想您可以累积一个已更改属性的列表,并将其作为通知的一部分发布,但这听起来不是很有用
还请注意,线程安全可能是您的问题,也可能不是。您可能需要使用锁定和/或Interlocked.Increment()
等
另一件事是,如果出现异常,您当然需要尝试/捕获您的呼叫以挂起/恢复。您可以通过编写一个实现IDisposable并在其Dispose中调用resume的包装器类来避免这种情况
代码可能如下所示:
public void DoStuff()
{
try
{
_entity.SuspendNotifications();
setProperties();
}
finally
{
_entity.ResumeNotifications();
}
}
private setProperties()
{
_entity.ID = 123454;
_entity.Name = "Name";
_entity.Description = "Desc";
}
[编辑] 如果要引入一个接口,比如说
isuspendablennotifications
,那么可以编写一个IDisposable
包装类来简化事情
下面的例子说明了这个概念;使用NotificationSuspender
简化了(实际上删除了)try/catch逻辑
请注意,类实体
当然不会实际实现挂起/恢复或提供任何错误处理;这是留给读者的谚语练习。:)
我认为这将是困难的,因为事件是异步触发的,但由执行线程同步处理。一种可能是使用
AutoResetEvent
或ManualResetEvent
并使用WaitOne
-方法等待Set
释放它。您可能需要将其与
互斥体结合使用。但是,如果您只在一个线程上工作,这将不起作用
有关手动重置事件
和自动重置事件
的信息,请参见假设所有事件使用相同的签名:
在MyEntity实例化时初始化一个委托,例如eventQueue
,并初始化一个int值,例如'queueRequiredLength'
每个属性设置器将其事件添加到队列中(如果尚未存在),eventQueue+=newEvent代码>而不是仅仅触发事件
然后,每个属性设置器检查队列的长度,并在(长度==queueRequiredLength){eventQueue();}
(我不知道如何检查委托中“排队”的方法的数量,但在最坏的情况下,您也可以保留一个计数器,并在每次添加到队列中时递增它)。我可以建议
public class MyEntity
{
private const int FieldsCount = 3;
private Int32 id;
private String name;
private String description;
private HashSet<string> dirty = new HashSet<string>();
public Int32 ID
{
get { return id; }
set
{
id = value;
dirty.Add("id");
GoListeners();
}
}
//...
private void GoListeners()
{
if (dirty.Count == FieldsCount)
{
//...
dirty.Clear();
}
}
}
公共类MyEntity
{
私家侦探int FIELDSCONT=3;
私有Int32 id;
私有字符串名称;
私有字符串描述;
private HashSet dirty=new HashSet();
公共Int32 ID
{
获取{return id;}
设置
{
id=值;
脏。添加(“id”);
GoListeners();
}
}
//...
私有侦听器()
{
if(dirty.Count==fieldscont)
{
//...
脏的,干净的;
}
}
}
在很大程度上取决于其他要求,例如,侦听器是否对单个属性更改采取行动,还是始终处理实体,而不管修改的数量和位置如何?为什么不使用标志?如果设置了标志1和2,但未设置标志3。。。什么都不要做。当事件触发并设置标志3时,然后处理您想要执行的任何代码。OT:我意识到我最初使用“using”语句的想法源自log4net实现,其中log4net.NDC.Push()调用的目的是将上下文消息推送到当前线程的上下文…这似乎是我最初对using语句的想法。我想我可以将事件添加到内部队列,然后在恢复通知时处理队列。如果是这样的话-我需要队列中的一些东西来让侦听器知道批处理何时完成…此模式的一个很好的MSDN示例是使用它的控件,这是
public class MyEntity
{
private const int FieldsCount = 3;
private Int32 id;
private String name;
private String description;
private HashSet<string> dirty = new HashSet<string>();
public Int32 ID
{
get { return id; }
set
{
id = value;
dirty.Add("id");
GoListeners();
}
}
//...
private void GoListeners()
{
if (dirty.Count == FieldsCount)
{
//...
dirty.Clear();
}
}
}