SoFunction
Updated on 2025-03-07

Detailed explanation of the implementation of IDispose interface in C# and why it is implemented in this way

Preface

I originally thought that for the implementation method of IDispose, it is enough to release unmanaged resources in it. However, through online information, I saw that many implementation methods are not just about releasing unmanaged resources. I was very confused. The key is that these information did not tell you in detail why you did this? Later, I learned about the reasons for this step by step through *. I said it in detail. Based on my own understanding, I will translate it and share it with you:

1. Implementation method of IDispose

For specific implementation methods, you can directly view this tutorial on my website:

https:///article/

If you can understand and know very well why you do that. Then you can omit the following articles. If you are not sure why you do that, please read with your confusion:

2. Why does it work like this

If you have good English, you can go to * directly to the original address of the original text:

/questions/538060/proper-use-of-the-idisposable-interface/538238#538238

2.1. Before proceeding

In C++, all memory space you apply for on the heap must be manually released, otherwise memory leakage will occur. This may make you spend some time writing programs on memory management rather than focusing on solving your programming purpose - solving problems. Therefore, as an evolutionary version of C++, C# uses GC (Garbage Collector) to manage memory to achieve the purpose of automatically releasing unnecessary memory, but GC cannot do it perfectly. GC is powerless for some unmanaged resources, which requires us to manually release unmanaged resources. In order to better do this, we need to write a method that can free unmanaged resources by manually calling this method.

Note:

What are managed and unmanaged resources?

Hosted resources are resources hosted to CLR, and CLR can manage these resources. Instead of non-managed resources, CLR cannot manage these resources, and the application and release of these resources must be managed by the user.

For example, resources such as file handles, context handles, windows or network connections in Win32 programming are all unmanaged resources. However, if these unmanaged resources are encapsulated in .Net and become part of the .Net class library, they are no longer unmanaged resources, because their automatic management function is implemented during the encapsulation process.

In other words, the objects generated by classes you can find in .Net are all managed resources.

(It is very important to understand this, which may be an important reason why you can't understand the above implementation tutorial!)

Note:

Time and order of garbage collection in GC?

We cannot determine the time when GC performs garbage collection (of course, except for your manual call to GC's garbage collection method), and the order cannot be determined! In other words, the space you applied for first may be released after the space you applied for later is released.

GC's methods of implementing destructors are different from classes that do not implement destructors. To put it simply, GC will definitely call their destructors for classes that implement destructors.

You can know so much about the garbage collection mechanism of .Net for now, and then go to the deepest understanding after reading this article.

2.2. We need to write a method to release it!

In order to clear some unmanaged resources, the class you create needs to have a public method, and the method name can be named at will.

For example:

public void Cleanup() 
public void Shutdown() 
…… 

You can do this, but there is a standard name

public void Dispose() 

There is even an interface IDisposeable, which contains the method just now

public interface IDisposable 
{ 
 void Dispose() 
} 

Therefore, the best way is to let your class implement the IDisposable interface and provide a piece of code to clear unmanaged resources in the Dispose method within the interface.

public void Dispose() 
{ 
 //Release a handle here (the handle is an unmanaged resource, which belongs to the concept of Win32 programming) (); 
} 

OK. This is done unless you want to do better!

2.3. Don’t forget that the hosting resources in the class still occupy space!

Hosted resources occupy space? The first thing you think of may be those hosting resources such as ints, strings, etc. They can occupy a few spaces, and they can occupy it!

But hosted resources are not just those resources. What if your object uses 250MB of Bitmap (this is in the .Net Frame and belongs to the managed resources) as some buffer? Of course, you know this is a .Net managed resource, so GC should have freed it. But do you really want to keep 250MB of memory space and be occupied? And wait for the GC to finally release it? What if there is a larger database connection? Of course we don’t want the connection to be used for nothing to wait for the end of GC!

If the user calls the Dispose method (meaning they no longer want to use everything in this object), why not throw away those bitmap resources and database connections that waste space?

Then, we should do this:

  • Free up unmanaged resources (because we have to)
  • Free up hosted resources (make your Dispose more perfect)

So, let's update our Dispose method to free up those managed resources

public void Dispose() 
{ 
 //Free unmanaged resources 
 (); 
 
 //Free managed resources too 
 if ( !=null) 
 { 
 (); 
  =null; 
 } 
 if ( !=null) 
 { 
 (); 
  = null; 
 } 
} 

OK, it's done well unless you want to do better!

2.4. There will always be people who carelessly forget to call Dispose!

What if someone creates an object using your class but forgets to call the Dispose method? This will leak some unmanaged resources!

Note: Forgot to call the Dispose method will cause the leakage of unmanaged resources, but for those managed resources, it will not be leaked, because the GC will eventually take action to free up the memory space related to the managed resources in the background thread. This includes the objects you create and the managed resources (such as Bitmap and database connections) (Why? Read on)

That is, if you forget to call the Dispose method, this class should automatically perform some remediation! We can think of designing a method as a backup method: using the final call of GC

Notice:Although GC will eventually release managed resources, GC does not know or care about your Dispose method. That's just a name we chose.

GC calls to the destructor is a perfect time to free up managed resources, and we implement the destructor function by overriding the Finalize method.

Notice:In C#, you can't really use the method of rewriting virtual functions to override Finalize. You can only write using syntax of destructors like C++, and the compiler will automatically make some changes to your code to implement the override finalizer method. (For details, please check the section on destructors in MSDN)

~MyObject() 
{ 
 //we're being finalized (), call Dispose in case the user forgot to 
 Dispose(); //<--Warning:subtle bug! Keep reading! 
} 

But there is a bug.

Just imagine that your GC is called in the background thread, and you have no control over the GC call time and the order of release of garbage objects. It is very likely that some managed resources of your object are released before the finalizer is called. When the GC calls the finalizer, the managed resources will eventually be released twice!

public void Dispose() 
{ 
 //Free unmanaged resources 
 (); 
 
 //Free managed resources too 
 if ( !=null) 
 { 
 (); //<-- crash, GC already destroyedit 
  =null; 
 } 
 if ( !=null) 
 { 
 (); //<-- crash, GC already destroyed it 
  = null; 
 } 
} 

So what you need to do is have the finalizer tell the Dispose method that it shouldn't release any managed resources again (because there's a good chance that these resources have been released!)

The standard Dispose mode allows the Dispose function and Finalize method to call the third method. This method has a bool-type parameter to indicate whether the call of this method is called through Dispose or GC. Implement specific resource release code in this method.

Of course, the naming of this third method can be named at will, but the standard method signature is:

protected void Dispose(Boolean disposing) 

Of course, the name of this parameter will make people unable to understand what it means. (Many online tutorials use this name, and it is difficult to understand the function of this variable for the first time)

This parameter may be written like this...

protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects) 
{ 
 //Free unmanaged resources 
 (); 
 
 //Free managed resources too, butonly if I'm being called from Dispose 
 //(If I'm being called fromFinalize then the objects might not exist 
 //anymore 
 if(itIsSafeToAlsoFreeManagedObjects) 
 {  
  if ( !=null) 
  { 
  (); 
    =null; 
  } 
  if ( !=null) 
  { 
  (); 
    =null; 
  } 
 } 
} 

In this way, the Dispose method in IDisposable becomes like this:

public void Dispose() 
{ 
 Dispose(true); //I am calling youfrom Dispose, it's safe 
} 

This is how the finalizer becomes...

~MyObject() 
{ 
 Dispose(false); //I am *not*calling you from Dispose, it's *not* safe 
} 

Notice:If your class inherits from another class, then don't forget to call the Dispose method of the parent class

public Dispose() 
{ 
 try 
 { 
  Dispose(true); //true: safeto free managed resources 
 } 
 finally 
 { 
  (); 
 } 
} 

Everything looks good unless, you want to do better!

2.5. The most perfect method?

If the user calls the Dispose method manually, everything is cleared. After that, because you rewrite the Finalize method, GC will definitely call this method, and it will call the Dispose method again!

This is not only a performance waste, but the key is that after the Dispose method is called, those references have become garbage, and GC will call these garbage references!

The solution is to prevent GC from calling Finalize method by calling () in the Dispose method.

public void Dispose() 
{ 
 Dispose(true); //I am calling youfrom Dispose, it's safe 
 (this); //Hey,GC: don't bother calling finalize later 
} 

In this way, everything is taken care of!

(Note:In fact, you can turn the Dispose (bool dispose) method into a virtual function, if your class is inherited)

At this point, we have implemented the best IDisposable method step by step. Now, let’s look at the tutorial on implementing the IDisposable interface at the beginning. Is it thorough?

3. Use the finalizer or the Dispose method to release unmanaged resources?

In fact, both methods are OK, but as mentioned at the beginning, the GC garbage collection time is uncertain. For those resources you no longer need, it is better to release them as soon as possible. You should not always wait for GC garbage collection. And another advantage is to reduce the time for GC garbage collection and improve efficiency. Why not do it?

Summarize

The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.