Standard
This article explains how you can write your own resource management code when you build a type that contains resources other than memory, especially when you are dealing with a non-memory resource.
We already know the importance of disposing of objects that occupy unmanaged (unmanaged) resources and should now write resource management code to dispose of those types that contain non-memory resources. Whole. NET Framework components use a standard pattern to handle non-memory resources. Users who use the type you build also want you to follow this standard pattern. The idea of standard processing mode is this: When the client remembers, use the IDisposable interface to release your unmanaged resources and use the Terminator (finalizer) when the client forgets. It works with the garbage collector (garbage Collector) to ensure that the object is affected by the finalizer-related performance only when necessary. This is a good way to deal with uncontrolled resources, so we should know it thoroughly.
The base class in a class hierarchy (root base class) must implement the IDisposable interface to free resources. This type must also add a finalizer as a defense mechanism. All of these programs delegate the task of releasing resources to a virtual method, which can be overloaded by a derived class based on its own resource management requirements. It needs to overload this virtual method as long as the derived class must free its own resources and it must call the base class version of the function.
At the beginning, if your class uses a non-memory resource, it must contain a finalizer. You cannot rely on the client to always invoke the Dispose () method. Because when they forget to do so, you are faced with the problem of resource leaks. It's their problem not to call Dispose, but you have a fault. The only way to ensure that a non-memory resource is released correctly is to establish a finalizer.
When the garbage collector is running, it immediately deletes all garbage objects without finalizers from memory. All objects with finalizers still exist in memory. These objects are added to the finalization queue, and the garbage collector raises a new thread that periodically runs finalizers on those objects. After these finalizer threads have completed their work, the garbage objects can be removed from memory. Objects that need to be terminated stay in memory much longer than objects that do not have finalizers. But you have no choice. If you want to make your program defensive, you must write a finalizer when the type contains unmanaged resources. But there is no need to worry about performance issues. The next step ensures that the client avoids the performance overhead associated with finalization.
Implementing the IDisposable interface is a standard way to notify users and the Run-time system that objects holding resources must be released in a timely manner. The IDisposable interface contains only one method:
public interface IDisposable
{
void Dispose ();
}
You are responsible for the following four transactions for the implementation of the IDisposable.Dispose () method (Implementation):
1, release all the uncontrolled resources.
2. Release all controlled resources (including unresolved events).
3, the setting flag indicates that the object has been processed. You must check this state flag in your own public method and throw a objectdisposed exception (if it is called again after an object has been processed).
4, prohibit the termination of operation (finalization). You call Gc.suppressfinalize (this) to complete this transaction.
By implementing the IDisposable interface you have completed two transactions: you provide a mechanism for clients to release all controlled resources they hold in a timely manner; You provide a standard way for clients to release uncontrolled resources. This is a great progress. When you implement the IDisposable interface in a type, the client can avoid the overhead of terminating the operation, and your class becomes. NET world of "the Citizen".
However, there are still some problems in the mechanism you have established. How can a derived class clean up its own resources while also allowing the base class to clean up resources? If the derived classes overload the finalization operation, or add their own IDisposable implementations, the methods must call the base class, otherwise the base class will not be properly cleaned up. Similarly, finalize (terminate operations) and dispose participate share some of the same responsibilities. The code for the Finalize method and the Dispose method is almost identical. And after overloading the interface functions, it doesn't work as you would expect. The third method in the standard dispose mode is a protected virtual helper function that decomposes these common transactions and adds a "hook" to the derived class for releasing the resource. The base class contains code for the core interface. As a response to a Dispose () or finalization operation, the virtual function provides a "hook" for scavenging resources for derived classes:
protected virtual void Dispose (bool isdisposing);
This overloaded method implements the necessary transactions that support finalize and dispose, and because it is virtual, it provides an entry point for all derived classes. Derived classes can overload this method, provide an appropriate implementation for clearing their own resources, and invoke the base class version. When Isdisposing is True (true), you can purge controlled and unmanaged resources, and when isdisposing is false, you can only clear uncontrolled resources. In either case, you can call the base class's Dispose (bool) method so that it clears its own resources.
Here's a short example that demonstrates the code framework that you provide when implementing this pattern. The Myresourcehog class demonstrates the code that implements the IDisposable interface, finalizers, and establishes a virtual Dispose method:
public class Myresourcehog:idisposable
{
Tags that have been processed
private bool _alreadydisposed = false;
Finalizers. Invoking the virtual Dispose method
~myresourcehog ()
{
Dispose (FALSE);
}
The realization of IDisposable
Invokes the virtual Dispose method. Prohibit finalization (finalization operation)
public void Dispose ()
{
Dispose (TRUE);
Gc. SuppressFinalize (TRUE);
}
Virtual Dispose method
protected virtual void Dispose (bool isdisposing)
{
Don't handle it more than once
if (_alreadydisposed)
Return
if (isdisposing)
{
TODO: Release Managed resources here
}
TODO: Frees uncontrolled resources here. Setting has been processed by tags
_alreadydisposed = true;
}
}
If a derived class needs to perform another cleanup operation, it should implement a protected Dispose method:
public class Derivedresourcehog:myresourcehog
{
It has its own processed markers.
private bool _disposed = false;
protected override void Dispose (bool isdisposing)
{
Don't handle it more than once
if (_disposed)
Return
if (isdisposing)
{
TODO: Release Managed resources here
}
TODO: Release all managed resources here
Let the base class release its own resources. The base class is responsible for calling Gc.suppressfinalize ()
Base. Dispose (isdisposing);
Set the processed markup of a derived class
_disposed = true;
}
}
Note that both the base class and the derived class contain the processed (disposed) markup for the object. This is purely a protective act. Copying this tag can encapsulate any possible errors that occur when all classes that make up an object release resources.
You must write protective dispose and finalize. objects can be processed in any order, and you may encounter an object that has already been processed before invoking the Dispose () method of its own type of member object. You should not think this is a problem because the Dispose () method is invoked multiple times. If it is invoked on an object that has already been processed, it does not perform any transactions. There are similar rules for Finalizer (finalizers). If the object you are referencing is still in memory, you do not need to check for null references (null reference). However, any object you refer to may be processed, and it may have been terminated.
This gives me the most important advice on any method that is relevant to handling or clearing: You should just release the resources and do not perform any other actions in the Dispose method. If you perform other operations in the Dispose or Finalize method, it can have serious adverse effects on the life cycle of the object. Objects are "Born" when they are constructed, and are "dead" when the garbage collector reclaims them. When your program is no longer able to access them, you can assume that they are in a "comatose" state. If you cannot reach an object, you cannot invoke its method, for all intents and purposes, it is dead. But there was a final breath before the object with the Terminator was declared dead. Finalizers should not perform any other operations other than to clean up uncontrolled resources. If a finalizer causes an object to arrive again for any reason, the object is restored (resurrected). Even if it wakes up from "sleeping", it is "alive". Here is an obvious example:
public class Badclass
{
To save a reference to a global object
Private ReadOnly ArrayList _finalizedlist;
private string _msg;
Public Badclass (ArrayList badlist, String msg)
{
Buffering the reference
_finalizedlist = badlist;
_msg = (string) Msg. Clone ();
}
~badclass ()
{
Add the object to the list. This object is reachable and is no longer rubbish. It's back!
_finalizedlist.add (this);
}
}
When a Badclass object executes its own finalizer, it adds its own reference to the global list. This only makes itself reachable, and it comes alive! But the problems caused by this operation will make anyone feel timid. The object has been terminated, so the garbage collector believes it is not necessary to call its finalizer again. When you really need to end a restored object, the end operation does not happen. Second, some of your resources may be out of use. GC does not remove any objects that can be reached by objects in the finalizer queue from memory, but it may have terminated those objects. If so, those objects must not be used again. Although the members of the Badclass are still in memory, they seem to have been processed or terminated. There is no way to control the end order in the C # language. You can't make this structure work more reliably. Don't try!
In addition to the college exercises, I have never seen the code so obvious to use the object being recovered. But I see some code that has this tendency to try to do some real work in finalizers, and it is turning objects into active states when certain functions called by the finalizer hold a reference to that object. In principle we have to examine very carefully any code in the finalizer and Dispose methods. If some code performs other operations other than releasing resources, we need to check again. These operations may cause bugs in the future. Remove these operations and make sure that the finalizer and Dispose () methods only release resources and do not perform other task transactions.
In a controlled environment, you do not have to write finalizers for each type you build, you only need to write finalizers for types that store unmanaged types, or those that contain members that implement the IDisposable interface. Even if you only need to disposable interface, do not need to finalizer, also should implement the whole pattern at the same time. Otherwise, you will complicate the implementation of the standard Dispose idea of the derived class, limiting the functionality of the derived class. Follow the standard dispose idea mentioned earlier, which will make life easier for you, users of your class, and users of derived classes from your type.