CA2000 Удалите предупреждение, когда ссылка хранится в ConcurrentDictionary - PullRequest
1 голос
/ 19 февраля 2020

У меня есть родительский класс, в котором есть коллекция дочерних классов. Оба класса IDisposable. Я получаю предупреждение CA2000 ("Call Dispose для объекта ... до того, как все ссылки на него выйдут из области видимости."), Но я не хочу Dispose() это в том же вызове метода, это создано, так как ребенок имеет долгую жизнь. Я держу ссылку на это в родительском классе. Если эта ссылка в Dictionary или ConcurrentBag, все хорошо. Если ссылка указана в ConcurrentDictionary, в списке указывается CA2000 .

Насколько я могу судить, я правильно реализую IDisposable. Возможно, способ, которым я итерирую параллельную коллекцию и вызываю Dispose(), мог бы быть улучшен, хотя я подозреваю, что это не источник предупреждения.

Это ошибка в компиляторе / предупреждениях, или я могу достичь соответствие через код? Мой опыт в теории состоит в том, что это синтаксис функции, используемый для добавления ссылки, но я не уверен.

В следующем примере воспроизводится предупреждение, с child1 и child3, все в порядке, но child2 выдает предупреждение, когда единственным отличием является тип контейнера:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

public class ParentDisposable : IDisposable
{
    private readonly Dictionary<string, ChildDisposable> dictionary = new Dictionary<string, ChildDisposable>();
    private readonly ConcurrentDictionary<string, ChildDisposable> concurrentDictionary = new ConcurrentDictionary<string, ChildDisposable>();
    private readonly ConcurrentBag<ChildDisposable> concurrentBag = new ConcurrentBag<ChildDisposable>();

    public void CreateChild()
    {
        // I want to create these disposable children, use them beyond the lifetime of this method call, and dispose of them when this parent class is disposed of
        var child1 = new ChildDisposable(); // No warning
        var child2 = new ChildDisposable(); // Warning CA2000  Call System.IDisposable.Dispose on object created by 'new ChildDisposable()' before all references to it are out of scope.
        var child3 = new ChildDisposable(); // No Warning
        this.dictionary.Add("key", child1);
        this.concurrentDictionary.AddOrUpdate("key", child2, (k, v) => child2);
        this.concurrentBag.Add(child3);
    }

    #region IDisposable Pattern
    private bool disposedValue = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposedValue)
        {
            if (disposing)
            {
                foreach (var key in this.dictionary.Keys)
                {
                    this.dictionary[key].Dispose();
                }

                foreach (var key in this.concurrentDictionary.Keys)
                {
                    this.concurrentDictionary[key].Dispose();
                }

                foreach (var child in this.concurrentBag)
                {
                    child.Dispose();
                }
            }

            this.disposedValue = true;
        }
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion IDisposable Pattern
}

public class ChildDisposable : IDisposable
{
    private EventWaitHandle waitObject = new EventWaitHandle(false, EventResetMode.AutoReset);

    #region IDisposable Pattern
    private bool disposedValue = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposedValue)
        {
            this.waitObject.Dispose();
            this.disposedValue = true;
        }
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion IDisposable Pattern
}

1 Ответ

2 голосов
/ 19 февраля 2020

Это не проблема в вашем коде, это упущение в используемой вами версии FxCop.

Это было исправлено в последней версии. См https://github.com/dotnet/roslyn-analyzers/issues/3082

...