C# 可观测采集中的块重入性<;T>;
请有人向我解释一下C# 可观测采集中的块重入性<;T>;,c#,.net,events,collections,observablecollection,C#,.net,Events,Collections,Observablecollection,请有人向我解释一下BlockReentrancy方法在observeCollection中的作用是什么 显示以下内容作为示例: //The typical usage is to wrap an OnCollectionChanged call within a using scope, as in the following example: using (BlockReentrancy()) { // OnCollectionChanged call } 但这似乎并没有为我阐明目
BlockReentrancy
方法在observeCollection
中的作用是什么
显示以下内容作为示例:
//The typical usage is to wrap an OnCollectionChanged call within a using scope, as in the following example:
using (BlockReentrancy())
{
// OnCollectionChanged call
}
但这似乎并没有为我阐明目的是什么。有人想解释一下吗?这是
blockreentancy()的实现。
还有另一种方法checkreentancy()
在修改集合之前,诸如ClearItems
,InsertItem
,MoveItem
,RemoveItem
,SetItem
检查CheckReentrancy()
/// <summary>
/// Disallow reentrant attempts to change this collection. E.g. an event handler
/// of the CollectionChanged event is not allowed to make changes to this collection.
/// </summary>
/// <remarks>
/// typical usage is to wrap e.g. a OnCollectionChanged call with a using() scope:
/// <code>
/// using (BlockReentrancy())
/// {
/// CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, index));
/// }
/// </code>
/// </remarks>
protected IDisposable BlockReentrancy()
{
_blockReentrancyCount++;
return EnsureMonitorInitialized();
}
/// <summary> Check and assert for reentrant attempts to change this collection. </summary>
/// <exception cref="InvalidOperationException"> raised when changing the collection
/// while another collection change is still being notified to other listeners </exception>
protected void CheckReentrancy()
{
if (_blockReentrancyCount > 0)
{
// we can allow changes if there's only one listener - the problem
// only arises if reentrant changes make the original event args
// invalid for later listeners. This keeps existing code working
// (e.g. Selector.SelectedItems).
if (CollectionChanged?.GetInvocationList().Length > 1)
throw new InvalidOperationException(SR.ObservableCollectionReentrancyNotAllowed);
}
}
private SimpleMonitor EnsureMonitorInitialized()
{
return _monitor ?? (_monitor = new SimpleMonitor(this));
}
因此,下面的代码保证不会使用在内部更改集合,但只有在订阅了多个处理程序的CollectionChanged
事件时才会更改集合
using BlockReentrancy())
{
CollectionChanged(this, e);
}
此示例演示了blockreentancy()
可重入性是指一个方法直接或间接地执行某些操作,从而导致再次调用该方法(可能是递归调用)。在这种情况下,如果要防止在处理程序中更改集合,则应在OnCollectionChanged委托内使用using块;尝试更改它将引发异常。如果未使用它,则任何修改集合的尝试都会导致再次调用OnCollectionChanged 一个observateCollection
实现了INotifyCollectionChanged
,因此它有一个CollectionChanged
事件。如果有此事件的订户,他们可以在集合已处于通知过程中时进一步修改该集合。由于CollectionChanged
事件精确跟踪更改的内容,因此这种交互可能会变得非常混乱
因此,observateCollection
作为特例,允许CollectionChanged
事件的单个订户从其处理程序修改集合。但是如果有两个或多个订阅者CollectionChanged
事件,则不允许从CollectionChanged
处理程序修改集合
using BlockReentrancy())
{
CollectionChanged(this, e);
}
这对方法blockreentancy
和CheckReentancy
用于实现此逻辑。blockreentancy
用于OnCollectionChanged
方法的开头,并且CheckReentancy
用于修改集合的所有方法。下面是blockreentancy的后面部分。在ObservableCollection的实现中,在每个集合修饰符方法的开头调用CheckReentrancy
/// <summary>
/// Disallow reentrant attempts to change this collection. E.g. an event handler
/// of the CollectionChanged event is not allowed to make changes to this collection.
/// </summary>
/// <remarks>
/// typical usage is to wrap e.g. a OnCollectionChanged call with a using() scope:
/// <code>
/// using (BlockReentrancy())
/// {
/// CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, index));
/// }
/// </code>
/// </remarks>
protected IDisposable BlockReentrancy()
{
_blockReentrancyCount++;
return EnsureMonitorInitialized();
}
/// <summary> Check and assert for reentrant attempts to change this collection. </summary>
/// <exception cref="InvalidOperationException"> raised when changing the collection
/// while another collection change is still being notified to other listeners </exception>
protected void CheckReentrancy()
{
if (_blockReentrancyCount > 0)
{
// we can allow changes if there's only one listener - the problem
// only arises if reentrant changes make the original event args
// invalid for later listeners. This keeps existing code working
// (e.g. Selector.SelectedItems).
if (CollectionChanged?.GetInvocationList().Length > 1)
throw new InvalidOperationException(SR.ObservableCollectionReentrancyNotAllowed);
}
}
private SimpleMonitor EnsureMonitorInitialized()
{
return _monitor ?? (_monitor = new SimpleMonitor(this));
}
(版权(C).NET基金会和贡献者)
您的演示代码将引发一个<代码> StAccOffFuffExist< <代码>,而不是<代码>无效操作异常> /代码>。在这种情况下,不检查可重入性。看到我的答案了。非常有趣的是,MSDN文档没有提到这个小事实,即CheckReentrancy只有在CollectionChanged事件连接了多个处理程序时才会阻止重入。“如果CollectionChanged事件有两个或多个订阅服务器,则不允许从CollectionChanged处理程序修改集合。”如果只有一个订阅者,但有两个工作线程在修改集合怎么办?在这种情况下,我应该使用另一个锁来锁定插入和移除等操作,并锁定OnCollectionChanged处理程序吗?宣布它为BlockReëentrancy
除了接受的答案之外,值得一提的是使用(blockreentancy()){}
在多线程用例中也是必需的,因为集合的更新如此频繁,以至于它干扰了OnCollectionChanged
处理。这可能只需要一个XAML绑定,而在自定义代码中没有任何额外的显式订阅服务器。
/// <summary>
/// Disallow reentrant attempts to change this collection. E.g. an event handler
/// of the CollectionChanged event is not allowed to make changes to this collection.
/// </summary>
/// <remarks>
/// typical usage is to wrap e.g. a OnCollectionChanged call with a using() scope:
/// <code>
/// using (BlockReentrancy())
/// {
/// CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, index));
/// }
/// </code>
/// </remarks>
protected IDisposable BlockReentrancy()
{
_blockReentrancyCount++;
return EnsureMonitorInitialized();
}
/// <summary> Check and assert for reentrant attempts to change this collection. </summary>
/// <exception cref="InvalidOperationException"> raised when changing the collection
/// while another collection change is still being notified to other listeners </exception>
protected void CheckReentrancy()
{
if (_blockReentrancyCount > 0)
{
// we can allow changes if there's only one listener - the problem
// only arises if reentrant changes make the original event args
// invalid for later listeners. This keeps existing code working
// (e.g. Selector.SelectedItems).
if (CollectionChanged?.GetInvocationList().Length > 1)
throw new InvalidOperationException(SR.ObservableCollectionReentrancyNotAllowed);
}
}
private SimpleMonitor EnsureMonitorInitialized()
{
return _monitor ?? (_monitor = new SimpleMonitor(this));
}