This article is transferred from:
When we use the unmanaged resources type, we should use the dispose () method of the idisposable interface to release resources. In the. NET environment, recycling of unmanaged resources is not the responsibility of the system. We must call the dispose () method to release the resources. The best way to ensure that unmanaged resources are released is to use using or try/finally.
All types of unmanaged resources implement the idisposable interface. In addition, when we do not explicitly release resources, for example, if we forget it, C # Will also guard to release resources by creating finalizer. If we want the application to run faster, we should release unnecessary resources as soon as possible. Fortunately, there are new keywords in C # to complete this task. Let's first consider the following code:
Save
public void ExcuteCommand(string connectString, string commandString){SqlConnection myConnection = new SqlConnection(connectString);SqlCommand myCommand = new SqlCommand(commandString, myConnection);myConnection.Open();myCommand.ExecuteNonQuery();}
Two objects are not released: sqlconnection and sqlcommand. They are all stored in the memory until the Terminator is called.
With the following modifications, we can release them:
Save
public void ExcuteCommand(string connectString, string commandString){SqlConnection myConnection = new SqlConnection(connectString);SqlCommand myCommand = new SqlCommand(commandString, myConnection);myConnection.Open();myCommand.ExecuteNonQuery();myCommand.Dispose();myConnection.Dispose();}
This is correct, provided that sqlcommand does not throw an exception. Once an exception occurs, our dispose () method will not run. The Using Keyword helps us ensure that dispose () is run. When we use using, the C # compiler converts it into a format similar to try/finally:
Save
public void ExcuteCommand(string connectString, string commandString){using (SqlConnection myConnection = new SqlConnection(connectString)){using (SqlCommand myCommand = new SqlCommand(commandString, myConnection)){myConnection.Open();myCommand.ExecuteNonQuery();}}}
The two sections of code in the following example will generate a very similar Il
Save
using (SqlConnection myConnection = new SqlConnection(connectString)){myConnection.Open();}try{SqlConnection myConnection = new SqlConnection(connectString);myConnection.Open();}finally{myConnection.Dispose();}
When we use unmanaged resources, using is a simple way to ensure reasonable resource release. If we use the using keyword for a type that does not support the idisposable interface, the compiler reports an error:
Save
// ErrorUsing(StringMSG ="This is a message") {Console. writeline (MSG );}
In addition, using only checks whether the compile-time type supports the idisposable interface, and does not recognize runtime objects. In the following example, even if the type returned by factory. createresource () supports the idisposable interface, compilation fails:
Save
// ErrorUsing(ObjectOBJ = factory. createresource ){}
We can handle objects that may not support the idisposable interface as follows:
Save
object obj = Factory.CreateResource();using(obj as IDisposable){}
If the object implements the idisposable interface, you can generate the code to release the resource. If this parameter is not supported, using (null) is generated, which is secure even though it does not work. If we are not sure whether the object should be put in using, it is safer to put it in.
When we use an unmanaged resource type in the program, we should put it in the brackets of using. When there are multiple resources to be released, such as connection and command in the preceding example, we should create multiple using, each containing a corresponding object. These using will be converted into different try/finally blocks. The effect is like the following code:
Save
public void ExcuteCommand(string connectString, string commandString){SqlConnection myConnection = null;SqlCommand myCommand = null;try{myConnection = new SqlConnection(connectString);try{myCommand = new SqlCommand(commandString, myConnection);myConnection.Open();myCommand.ExecuteNonQuery();}finally{if (myCommand != null){myCommand.Dispose();}}}finally{if (myConnection != null){myConnection.Dispose();}}}
Each using Declaration creates a try/Finally block. We can also cancel multi-layer nesting through writing like this:
Save
public void ExcuteCommand(string connectString, string commandString){SqlConnection myConnection = null;SqlCommand myCommand = null;try{myConnection = new SqlConnection(connectString);myCommand = new SqlCommand(commandString, myConnection);myConnection.Open();myCommand.ExecuteNonQuery();}finally{if (myCommand != null){myCommand.Dispose();}if (myConnection != null){myConnection.Dispose();}}}
Although it looks simple, we should not use the using statement like this:
Save
public void ExcuteCommand(string connectString, string commandString){SqlConnection myConnection = new SqlConnection(connectString);SqlCommand myCommand = new SqlCommand(commandString, myConnection);using (myConnection as IDisposable){using (myCommand as IDisposable){myConnection.Open();myCommand.ExecuteNonQuery();}}}
This is a potential bug. Once the sqlcommand constructor throws an exception, sqlconnection cannot be released. We must ensure that each unmanaged resource object can be smoothly released, otherwise it may cause a waste of memory resources. The using keyword is the best method for a single unmanaged resource object. For multiple objects, we can use the nested using or try/finally writing method to release resources.
There is also a detail in releasing resources. Some types include not only the dispose () method, but also the close () method, such as sqlconnection. In this way, we can close the sqlconnection connection:
Save
myConnection.Close();
In this way, the connection can be released, but it is not the same as the dispose () method. In addition to releasing resources, the dispose () method also has other tasks: It notifies the Garbage Collector (Garbage Collector) that the resource of this object has been released, you do not have to perform repeated operations in the Terminator. Dispose () calls the GC. suppressfinalize () method. This method requests the system not to call the terminator of the specified object. But close () is not like this. The object released with close () does not have to call the Terminator, but it still exists in the resource release queue of the Terminator. Dispose () is more thorough than close.
Dispose () does not remove objects from the memory. It adds a hook for the object to release resources ). This means that we can use dispose () for the objects in use. We should be careful with this.
Most types in C # do not support dispose (). Among the more than 1500 types, only 100 implement the idispose interface. When we use an object that implements this interface, we should release resources by using the using or try/Finally block method as appropriate.
Translated from Objective C #: 50 specific ways to improve your C # by Bill Wagner
Back to directory
P.s. Personal thoughts are as follows:
Both dispose () and close () can display the call to release resources. Finalize () is protected, and the Destructor do not know when it will be executed. Objects that are closed () may be revived again. For example, sqlconnection can still be used through open (), as if they were in sleep state. The dispose () object should not be revived theoretically, because at the same time, we should tell the GC that the object has been over, to avoid repeated object cleanup. Of course, we can continue to access the object by getting the referenced method when releasing the resource, but because dispose () calls GC. the suppressfinalize () method is exempt from termination, so the resources of these dispose () and resurrected objects will no longer be automatically released by GC.
Save
Public ClassDisposeclass: idisposable {Private StringInsideresources;Public VoidUseinsideresources () {insideresources ="Use Resouces"; Console. writeline (insideresources );}PublicDisposeclass () {console. writeline ("Create object");}~ Disposeclass () {console. writeline ("Destroy ~ Object");}# Region idisposable MemberPublic VoidDispose (){// Todo: add the disposeclass. Dispose implementation.Console. writeline ("Dispose object"); GC. suppressfinalize (This);}# Endregion}
The class in the preceding example does not call the Destructor after the resource is released using dispose (). If dispose () is not called, The Destructor will be called when it needs to be terminated. But if we use it like this:
Save
static void Main(string[] args){ArrayList myList = new ArrayList();using (DisposeClass a = new DisposeClass()){myList.Add(a);}((DisposeClass) (myList[0])).UseInsideResources();Console.ReadLine();}
The objects that have been dispose are resurrected. And the Destructor will not be called again. We can also do this in destructor, but we don't know when it will happen.
Finalize () is a magic function. When there is no destructor, it can be used as a common member function like ABC (), but once the Destructor exists, the error "The finalize () member already exists" is reported. What's even more strange is that when we declare it as a Member protection function (I declared it as public at the beginning, and there will be no special results, but it will be changed to protected later, it is called by the destructor of its derived class. In C #, The destructor of the derived class automatically calls the destructor of the base class. Therefore, the derived class regards it as a base class destructor. In the compiler, it should be equal to the destructor, but object. Finalize () obviously cannot be rewritten. For protected void finalize (), what is it? This should be an error that can be found at the compilation stage, right? The Code is as follows:
Save
Static VoidMain (String[] ARGs) {extenddisposeclass A =NewExtenddisposeclass (); console. Readline ();}Public ClassDisposeclass: idisposable {PublicDisposeclass () {console. writeline ("Create object");}//~ Disposeclass () //{ // Console. writeline ("Destroy ~ Object "); //}Protected VoidFinalize () {console. writeline ("Finalize object");}# Region idisposable MemberPublic VoidDispose (){// Todo: add the disposeclass. Dispose implementation.Console. writeline ("Dispose object");}# Endregion}Public ClassExtenddisposeclass: disposeclass {~ Extenddisposeclass () {console. writeline ("Destroyextenddisposeclass object");}}
Releasing resources is really not easy...