5. Processing in custom Mode
This is helpful when you consider what will happen if TextReader does not implement the IDisposable interface. This tutorial will show you how to implement Dispose in our own classes. One way is to use the Object Adapter mode. For example:
Public sealed class AutoTextReader: IDisposable
{
Public AutoTextReader (TextReader target)
{
// PreCondition (target! = Null );
Adaptee = target;
}
Public TextReader
{
Get {return adaptee ;}
}
Public void Dispose ()
{
Adaptee. Close ();
}
Private readonly TextReader adaptee;
}
You can use your own class as follows:
Using (AutoTextReader scoped = new AutoTextReader (file. OpenText ()))
{
Scoped. TextReader. Read (source, 0, length );
}
You can use implicit conversion operators to simplify the problem:
Public sealed class AutoTextReader: IDisposable
{
...
Public static implicit operator AutoTextReader (TextReader target)
{
Return new AutoTextReader (target );
}
...
}
This will allow you to use it like this:
Using (AutoTextReader scoped = file. OpenText ())
{
Scoped. TextReader. Read (source, 0, length );
}
Struct: Another option
AutoTextReader intentionally uses it as a sealed class and considers its name as recommended to be used as a local variable. It is more meaningful to use a structure to replace classes:
Public struct AutoTextReader: IDisposable
{
// Exactly as before
}
Using a structure to replace classes also provides several free optimizations. Since a structure is a value type, it can never be a null value. This means that the compiler must check the generated finally block for null values. In addition, since you cannot inherit from a structure, its runtime will be consistent with the type during compilation. This means that the compiler generally performs forced conversion in the finally block generated and thus avoids a packing operation (especially, if Dispose is a public implicit interface implementation rather than an undisclosed display interface implementation, this will avoid forced conversion ).
In other words, it is like this:
Using (AutoTextReader scoped = file. OpenText ())
{
Scoped. TextReader. Read (source, 0, length );
}
Converted:
{
AutoTextReader scoped = new file. OpenText ();
Try
{
Scoped. TextReader. Read (source, 0, length );
}
Finally
{
Scoped. Dispose ();
}
}
Therefore, I prefer to use the using statement instead of the finally program for processing. In fact, the using statement solution has the following advantages over the earlier "ideal" version: A using statement:
· Running, it can always release resources
· Is an extension mechanism. It allows you to create a set of resources to release. It is easy to create your own resource release class, such as AutoTextReader.
· Allows you to pair Resource Acquisition with resource release. The best time to release resources is when you get resources. Just like if you borrowed a book from the library, you can be told when to return it .]
· The syntax structure clearly tells you that you are using a resource.
· Create a range for variables with resources. Observe the compiler conversion of the using statement carefully and you will find that it uses an external pair of parentheses intelligently.
Using (AutoTextReader scoped = file. OpenText ())
{
Scoped. TextReader. Read (source, 0, length );
}
Scoped. TextReader. Close (); // scoped is not in scope here
This is a trace of the condition declaration in C ++. It allows you to limit the range of variables used. variables are useful only in this range. When a variable can be used, they can only exist in this range.