To use an unmanaged resource type, you must implement the idisposable interface's dispose () method to precisely release system resources .. This rule in the. NET environment causes the release of resources.Source codeIs a type of user, rather than a type or system. Therefore, whenever you use a type with the dispose () method, you have the responsibility to call the dispose () method to release resources. The best way to ensure that dispose () is to use the using statement or try/Finally block.
All types that contain unmanaged resources should implement the idisposable interface. In addition, when you forget to properly process these types, they will passively create destructor. If you forget to process these objects, those non-memory resources will be released later when the Destructor are called accurately. This makes these objects wait for a longer time in the memory, which will make your applicationProgramThe speed will decrease because the system resources are occupying too much resources.
Fortunately, the precise release of resources by C # Language designers is a common task. They added a keyword to make it easy.
Suppose you wrote the followingCode:
Public void executecommand (string connstring, string commandstring)
{
Sqlconnection myconnection = new sqlconnection (connstring );
Sqlcommand mysqlcommand = new sqlcommand (commandstring,
Myconnection );
Myconnection. open ();
Mysqlcommand. executenonquery ();
}
Two processable objects in this example are not properly released: sqlconnection and sqlcommand. Both objects are stored in the memory until the Destructor is called. (Both classes are inherited from system. componentmodel. component .)
The solution to this problem is to call their dispose after using the command and link:
Public void executecommand (string connstring, string commandstring)
{
Sqlconnection myconnection = new sqlconnection (connstring );
Sqlcommand mysqlcommand = new sqlcommand (commandstring,
Myconnection );
Myconnection. open ();
Mysqlcommand. executenonquery ();
Mysqlcommand. Dispose ();
Myconnection. Dispose ();
}
This is good, unless the SQL command throws an exception during execution, your dispose () call will never succeed. The Using statement ensures that the dispose () method is called. When you allocate objects to the using statement, the C # compiler places these objects in a try/Finally block:
Public void executecommand (string connstring, string commandstring)
{
Using (sqlconnection myconnection = new sqlconnection (connstring ))
{
Using (sqlcommand mysqlcommand = new sqlcommand (commandstring, myconnection ))
{
Myconnection. open ();
Mysqlcommand. executenonquery ();
}
}
}
When you use a processing object in a function, the using statement is the easiest way to ensure that the object is properly processed. When these objects are allocated, they will be put into a try/Finally block by the compiler. The following two pieces of code are compiled into the same Il:
Sqlconnection myconnection = NULL;
// Example using clause:
Using (myconnection = new sqlconnection (connstring ))
{
Myconnection. open ();
}
// Example try/Catch Block:
Try {
Myconnection = new sqlconnection (connstring );
Myconnection. open ();
}
Finally {
Myconnection. Dispose ();
}
In my personal experience in using try/catch/finally blocks, I think the above practice is very inconvenient. Resources can be released, but errors cannot be found. For more information about how to throw exceptions and release resources at the same time, see other related resources, such as Jeffrey's. NET Framework Program Design and revision)
If you place a variable that cannot be processed in the using statement, the C # compiler gives an error, for example:
// Does not compile:
// String is sealed, and does not support idisposable.
Using (string MSG = "this is a message ")
Console. writeline (MSG );
Using can only be used when compiling, And the types that support the idispose interface can be used, not any objects:
// Does not compile.
// Object does not support idisposable.
Using (Object OBJ = factory. createresource ())
Console. writeline (obj. tostring ());
If OBJ implements the idispose interface, the using statement will generate the resource cleaning code. If it is not, using will degrade to using (null), which is safe, but has no effect. If you are not sure whether an object should be placed in the using statement, it is better to be safer: Suppose you want to do this and put it in the using statement in the previous method.
Here is a simple case: Whenever you use a processing object in a method, put this object in the using statement. Now you can learn more complex applications. In the preceding example, we need to release two objects: the connection object and the command object. The preceding example shows that two different using statements are created, one containing a processing object. Each using statement generates a different try/Finally block. You write the following code:
Public void executecommand (string connstring, string commandstring)
{
Sqlconnection myconnection = NULL;
Sqlcommand mysqlcommand = NULL;
Try
{
Myconnection = new sqlconnection (connstring );
Try
{
Mysqlcommand = new sqlcommand (commandstring,
Myconnection );
Myconnection. open ();
Mysqlcommand. executenonquery ();
}
Finally
{
If (mysqlcommand! = NULL)
Mysqlcommand. Dispose ();
}
}
Finally
{
If (myconnection! = NULL)
Myconnection. Dispose ();
}
}
Each using statement generates a new nested try/Finally block. I found that this is a very bad structure, so if you encounter multiple objects that implement the idisposable interface, I prefer to write my own try/Finally block:
Public void executecommand (string connstring, string commandstring)
{
Sqlconnection myconnection = NULL;
Sqlcommand mysqlcommand = NULL;
Try {
Myconnection = new sqlconnection (connstring );
Mysqlcommand = new sqlcommand (commandstring,
Myconnection );
Myconnection. open ();
Mysqlcommand. executenonquery ();
}
Finally
{
If (mysqlcommand! = NULL)
Mysqlcommand. Dispose ();
If (myconnection! = NULL)
Myconnection. Dispose ();
}
}
The author's judgment on whether an object is null is very important. In particular, some objects encapsulated with COM are implicitly released, an exception occurs when you release some empty objects. For example, if one COM is referenced by variables of two different interfaces and dispose is called on one of them, the other call will fail. In. net, you should also pay attention to this problem, so you must determine whether the object is null)
However, please do not try to use as to write such a using statement:
Public void executecommand (string connstring, string commandstring)
{
// Bad idea. potential resource leak lurks!
Sqlconnection myconnection = new sqlconnection (connstring );
Sqlcommand mysqlcommand = new sqlcommand (commandstring, myconnection );
Using (myconnection as idisposable)
Using (mysqlcommand as idisposable)
{
Myconnection. open ();
Mysqlcommand. executenonquery ();
}
}
This seems refreshing, but there is a tricky bug. If the sqlcommand () constructor throws an exception, the sqlconnection object cannot be processed. You must ensure that each object implementing the idispose interface is allocated within the using range or within the try/Finally block. Otherwise, resource leakage may occur.
So far, you have learned the two most common cases. Whenever an object is processed in a method, using the using statement is the best way to ensure that the requested resources are released in various situations. When you assign multiple (implemented idisposable Interface) objects to a method, create multiple using blocks or use your own try/finally blocks.
There is a slight difference in understanding the objects that can be processed. Some objects support both disponse and close methods to release resources. Sqlconnection is one of them. You can close sqlconnection as follows:
Public void executecommand (string connstring, string commandstring)
{
Sqlconnection myconnection = NULL;
Try {
Myconnection = new sqlconnection (connstring );
Sqlcommand mysqlcommand = new sqlcommand (commandstring,
Myconnection );
Myconnection. open ();
Mysqlcommand. executenonquery ();
}
Finally
{
If (myconnection! = NULL)
Myconnection. Close ();
}
}
This version closes the link, but it is indeed different from the processing object. The dispose method releases more resources and tells GC that this object no longer needs to be destructed, refer to other books ). Dispose calls GC. suppressfinalize (), but close () generally does not. The result is that the object is queued in the Destructor queue, even if the Destructor is not required. When you have a choice, dispose () is better than colse. You will learn more exciting content in Principle 18.
Dispose () does not remove objects from the memory. It is a hook for releasing an object from an unmanaged resource. This means that you may encounter such a problem: releasing an object that is still in use. Do not release an object that is still referenced elsewhere in the program.
In some cases, resource management in C # is more difficult than C ++. You cannot expect definite destructor to clear all the resources you use. However, the garbage collector makes it easier for you to implement the idisposable interface without having to implement the big data type. Of the more than 1500 classes in the. NET Framework, less than 100 classes implement the idisposable interface. When you use an object that implements the idisposeable interface, remember to process them in all classes. You should include them in using statements or try/finally blocks. No matter which one is used, make sure that the objects are correctly released every moment.