Подтвердить что ты не робот

BlockReentrancy в ObservableCollection <T>

Может кто-нибудь, пожалуйста, будьте любезны, чтобы объяснить мне, что цель метода BlockReentrancy находится в ObservableCollection<T>?

MSDN показывает в качестве примера следующее:

//The typical usage is to wrap an OnCollectionChanged call within a using scope, as in the following example:

using (BlockReentrancy())
{
    // OnCollectionChanged call
}

Но это, похоже, не разъясняет мне, в чем цель. Кто-нибудь должен объяснить?

4b9b3361

Ответ 1

An ObservableCollection реализует INotifyCollectionChanged и поэтому имеет событие CollectionChanged. Если есть подписчик на это событие, они могут дополнительно изменить коллекцию, пока коллекция уже находится в процессе уведомления. Поскольку событие CollectionChanged отслеживает то, что изменилось, это взаимодействие может стать очень грязным.

В результате ObservableCollection разрешает в качестве отдельного случая один абонент события CollectionChanged изменять коллекцию из своего обработчика. Но запрещает изменять коллекцию из обработчика CollectionChanged , если в событие CollectionChanged есть два или более подписчиков.

Пара методов BlockReentrancy и CheckReentancy используется для реализации этой логики. BlockReentrancy используется в начале метода OnCollectionChanged, а CheckReentancy используется во всех методах, которые изменяют коллекцию.

Ответ 2

Это реализация BlockReentrancy()

protected IDisposable BlockReentrancy()
{
   this._monitor.Enter();
   return this._monitor;
}

Существует еще один метод CheckReentrancy()

protected void CheckReentrancy()
{
    if ((this._monitor.Busy && (this.CollectionChanged != null)) && (this.CollectionChanged.GetInvocationList().Length > 1))
    {
        throw new InvalidOperationException(SR.GetString("ObservableCollectionReentrancyNotAllowed"));
    }
}

Такие методы, как ClearItems, InsertItem, MoveItem, RemoveItem, SetItem проверяют CheckReentrancy() перед изменением коллекции.

Таким образом, приведенный ниже код гарантирует, что коллекция не будет изменена внутри using, но только если есть более одного обработчика, подписанного на событие CollectionChanged.

using BlockReentrancy())
{
    CollectionChanged(this, e);
}

Этот пример демонстрирует эффект BlockReentrancy()

private static void Main()
{
    collection.CollectionChanged += CollectionCollectionChanged1;
    collection.CollectionChanged += CollectionCollectionChanged2;
    collection.Add(1);
}

private static void CollectionCollectionChanged1(object sender, NotifyCollectionChangedEventArgs e)
{
    collection.Add(2); // this line will throw exception
}

private static void CollectionCollectionChanged2(object sender, NotifyCollectionChangedEventArgs e)
{
}

Ответ 3

Reentrancy - это когда метод делает что-то прямо или косвенно, что вызывает повторный вызов этого метода, возможно, рекурсивно. В этом случае используемый блок должен использоваться внутри делегата OnCollectionChanged, если вы хотите предотвратить изменение коллекции из обработчика; попытки изменить это вызовут исключение. Если вы его не использовали, любые попытки изменить коллекцию вызовут OnCollectionChanged для повторного вызова.

Ответ 4

Ниже приведен код позади BlockReentrancy. CheckReentrancy вызывается в начале каждого метода модификатора коллекции в реализации ObservableCollection.

    /// <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 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));
    }

(Copyright (c).NET Foundation и Авторы)