Как правильно проектировать объекты с внутренними ссылками на одноразовые объекты - PullRequest
0 голосов
/ 17 марта 2012

Предположим, у меня есть следующие классы:

public class DisposableObj : IDisposable
{
    public ChildObj CreateObj();
    internal object GetSomething();
    // ...
}

public class ChildObj
{
    private DisposableObj m_provider;

    public void DoSomething()
    {
        m_provider.GetSomething();
    }
    // ...
}

Возможно, что в какой-то момент одноразовый объект будет удален, но дочерний объект все равно будет иметь ссылку на него.

Если в это время пользователь вызовет метод DoSomething, тогда дочерний объект попытается получить доступ к удаленному объекту. Это не хорошо, поэтому вопрос:

Как правильно проектировать такие классы?

UPDATE / ПОЯСНЕНИЯ:

Мне известно об исключении ObjectDisposedException и всем остальном. Мой вопрос, вероятно, должен звучать так: как правильно уведомить пользователя об исключительной ситуации и как спроектировать классы, чтобы упростить их обслуживание?

Ответы [ 2 ]

2 голосов
/ 17 марта 2012

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

Сказав это, проясните в своем проекте, кто отвечает за удаление DisposableObj, и когда - если какой-либо ребенок получит доступ к удаленному объекту впоследствии, вы можете утверждать, что этот должен вызвать исключение - не обходите сторонойэто, но выдает исключение и позволяет всплыть исключению, чтобы вы могли исправить логику при обнаружении проблемы.

С точки зрения реализации вы можете добиться этого, просто сохраняя логическое значение, которое отслеживает, является ли DisposableObjизбавиться и на более поздний доступ просто бросить ObjectDisposedException.Чтобы уточнить, я имею в виду, что DisposableObj объект сам должен отслеживать его состояние и генерировать ObjectDisposedException при любом вызове метода после его удаления.

2 голосов
/ 17 марта 2012

Первое, что приходит на ум:
Предоставьте вашему классу ChildObj внутреннее логическое свойство ProviderDisposed.
Установите для этого свойства значение true из Dispose в вашем DisposableObj

Однако вы должны вести списокобъектов, созданных для связи каждого из них с состоянием вашего основного объекта.

List<ChildObj> childsCreated = new List<ChildObj>();
public ChildObj CreateObj()
{
    ChildObj obj = new ChildObj();
    childsCreated.Add(obj);
    return obj;
}
public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
    // Check to see if Dispose has already been called.
    if(!this.disposed)
    {
        if(disposing)
        {
            foreach(ChildObj obj in childsCreated)
                obj.ProviderDisposed = true;
            childsCreated = null;
        }
        disposed = true;
    }
}

public class ChildObj   
{   
    private DisposableObj m_provider;   
    private bool m_providerDisposed = false;

    public bool ProviderDisposed 
    { set { m_providerDisposed = true; } }

    public void DoSomething()   
    {   
        if(m_providerDisposed == false)
             m_provider.GetSomething();
        // else // as from **@BrokenGlass answer**
        //     throw new ObjectDisposedException();   
    }   
    // ...   
}  
...