📜 ⬆️ ⬇️

About poor Dispose put in a word (part 2)

After the publication of my previous Dispose entry, several hot issues emerged in the discussion that required separate consideration.

In short, they come down to “why is it so difficult?” And “how should descendants properly free their memory?”

And in fact. There are two implementations. Simple:
DBConnection conn = new DBConnection();
try
{
conn.Open();
//...
}
finally
{
conn.Close();
}


* This source code was highlighted with Source Code Highlighter .
And complicated:
public class MyResource: IDisposable
{
private bool disposed = false ;

// IDisposable
//
//
public void Dispose()
{
Dispose( true ); //
GC.SuppressFinalize( this ); // , GC
}

//
~MyClass()
{
Dispose( false ); // GC
}

//
private void Dispose( bool manual)
{
if (! this .disposed)
{
if (manual)
{
// managed
//...

// - Dispose
// managed -
// Dispose,
// Dispose GC
}

// unmanaged
//...

disposed = true ;
}
}
}


* This source code was highlighted with Source Code Highlighter .
Moreover, a complex implementation exists in two versions. The first option offers MSDN on this link, and the second (to support inheritance) here on this one . The second implementation looks like this:
public class Base: IDisposable
{
public void Dispose()
{
Dispose( true );
GC.SuppressFinalize( this );
}

protected virtual void Dispose( bool disposing)
{
if (disposing)
{
// Dispose managed
}
// unmanaged
}

~Base()
{
Dispose ( false );
}
}

//
public class Derived: Base
{
protected override void Dispose( bool disposing)
{
if (disposing)
{
// Dispose managed
}
// unmanaged

// Dispose(bool)
base .Dispose(disposing);
}

// Dispose
// - .
}


* This source code was highlighted with Source Code Highlighter .
The difference, as is easy to see, is that the Dispose (bool) method has turned from closed to protected and virtual. Let's try to figure out why it is so difficult? And why is it necessary to know?
')
I will answer the second question first: you need to know the proposed option for freeing resources if you inherit from the standard classes that have already implemented the IDisposable interface. An example would be System.Windows.Forms.UserControl, which already has the Dispose method implemented.

Simple case: you spawned your control from UserControl. And your control owns an unmanaged resource. For example, he knows how to display an image using OpenGL and for this he needs to control the rendering context. And not only manage, but also correctly release it. In this case, by overriding the Dispose method from the parent class, you intervene in the resource release scheme, which is obviously bad.

In this case, you can solve the problem by interacting with the resource release mechanism proposed by MS. And their solution looks quite simple:

1) There is a method for manual cleaning "right now and immediately": Dispose ()
2) There is a method to clear the "time to scavenge" (called from GC): destructor
3) Both the one and the other method must perform the cleaning, which is in a separate method: Dispose (bool)

The Dispose (bool) method, which performs the cleanup, receives an input to the input that allows to distinguish the cleanup from “right now and immediately” from “it's time to clean up the garbage”. What for? And because your class can own managed member variables that own unmanaged resources. And so that they can release unmanaged resources, they also need to call Dispose. Unlike a situation when GC is engaged in cleaning. In this case, there was no demand to release resources “right now and immediately” and you can rely on GC.

In the implementation proposed for the case of inheritance from MSDN, there is one more feature: there is no flag-variable disposed. Those. no protection against multiple call Dispose. What is not very beautiful. There is a slightly different implementation suggested in this article:
using System;

namespace RSDN
{
public abstract class DisposableType: IDisposable
{
bool disposed = false ;

~DisposableType()
{
if (!disposed)
{
disposed = true ;
Dispose( false );
}
}

public void Dispose()
{
if (!disposed)
{
disposed = true ;
Dispose( true );
GC.SuppressFinalize( this );
}
}

public void Close()
{
Dispose();
}

protected virtual void Dispose( bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
}


* This source code was highlighted with Source Code Highlighter .

Source: https://habr.com/ru/post/57432/


All Articles