Tải bản đầy đủ - 0 (trang)
13-6. Implement a Disposable Class

13-6. Implement a Disposable Class

Tải bản đầy đủ - 0trang

CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



As a complementary mechanism to using finalizers, the .NET Framework defines the Dispose

pattern as a means to provide deterministic control over when to free unmanaged resources. To

implement the Dispose pattern, a class must implement the IDisposable interface, which declares a

single method named Dispose. In the Dispose method, you must implement the code necessary to

release any unmanaged resources and remove the object from the list of objects eligible for finalization if

a finalizer has been defined.

Instances of classes that implement the Dispose pattern are called disposable objects. When code

has finished with a disposable object, it calls the object’s Dispose method to free all resources and make

it unusable, but still relies on the garbage collector to eventually release the object memory. It’s

important to understand that the runtime does not enforce disposal of objects; it’s the responsibility of

the client to call the Dispose method. However, because the .NET Framework class library uses the

Dispose pattern extensively, C# provides the using statement to simplify the correct use of disposable

objects. The following code shows the structure of a using statement:

using (FileStream fileStream = new FileStream("SomeFile.txt", FileMode.Open)) {

// Do something with the fileStream object.

}

When the code reaches the end of the block in which the disposable object was declared, the

object’s Dispose method is automatically called, even if an exception is raised. Here are some points to

consider when implementing the Dispose pattern:





Client code should be able to call the Dispose method repeatedly with no adverse

effects.







In multithreaded applications, it’s important that only one thread execute the

Dispose method at a time. It’s normally the responsibility of the client code to

ensure thread synchronization, although you could decide to implement

synchronization within the Dispose method.







The Dispose method should not throw exceptions.







Because the Dispose method does all necessary cleaning up, you do not need to

call the object’s finalizer. Your Dispose method should call the

GC.SuppressFinalize method to ensure that the finalizer is not called during

garbage collection.







Implement a finalizer that calls the unmanaged cleanup part of your Dispose

method as a safety mechanism in case client code does not call Dispose correctly.

However, avoid referencing managed objects in finalizers, because you cannot be

certain of the object’s state.







If a disposable class extends another disposable class, the Dispose method of the

child must call the Dispose method of its base class. Wrap the child’s code in a try

block and call the parent’s Dispose method in a finally clause to ensure

execution.







Other instance methods and properties of the class should throw a

System.ObjectDisposedException exception if client code attempts to execute a

method on an already disposed object.



648



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



The Code

The following example demonstrates a common implementation of the Dispose pattern.

using System;

namespace Apress.VisualCSharpRecipes.Chapter13

{

// Implement the IDisposable interface.

public class DisposeExample : IDisposable

{

// Private data member to signal if the object has already been

// disposed.

bool isDisposed = false;

// Private data member that holds the handle to an unmanaged resource.

private IntPtr resourceHandle;

// Constructor.

public DisposeExample()

{

// Constructor code obtains reference to unmanaged resource.

resourceHandle = default(IntPtr);

}

// Destructor/finalizer. Because Dispose calls GC.SuppressFinalize,

// this method is called by the garbage collection process only if

// the consumer of the object does not call Dispose as it should.

~DisposeExample()

{

// Call the Dispose method as opposed to duplicating the code to

// clean up any unmanaged resources. Use the protected Dispose

// overload and pass a value of "false" to indicate that Dispose is

// being called during the garbage collection process, not by

// consumer code.

Dispose(false);

}

// Public implementation of the IDisposable.Dispose method,

// by the consumer of the object in order to free unmanaged

// deterministically.

public void Dispose()

{

// Call the protected Dispose overload and pass a value

// to indicate that Dispose is being called by consumer

// by the garbage collector.

Dispose(true);



called

resources



of "true"

code, not



// Because the Dispose method performs all necessary cleanup,

// ensure the garbage collector does not call the class destructor.

GC.SuppressFinalize(this);

}



649



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



// Protected overload of the Dispose method. The disposing argument

// signals whether the method is called by consumer code (true), or by

// the garbage collector (false). Note that this method is not part of

// the IDisposable interface because it has a different signature to the

// parameterless Dispose method.

protected virtual void Dispose(bool disposing)

{

// Don't try to dispose of the object twice.

if (!isDisposed)

{

// Determine if consumer code or the garbage collector is

// calling. Avoid referencing other managed objects during

// finalization.

if (disposing)

{

// Method called by consumer code. Call the Dispose method

// of any managed data members that implement the

// IDisposable interface.

// ...

}

//

//

//

//



Whether called by consumer code or the garbage collector,

free all unmanaged resources and set the value of managed

data members to null.

Close(resourceHandle);



// In the case of an inherited type, call base.Dispose(disposing).

}

// Signal that this object has been disposed.

isDisposed = true;

}

// Before executing any functionality, ensure that Dispose has not

// already been executed on the object.

public void SomeMethod()

{

// Throw an exception if the object has already been disposed.

if (isDisposed)

{

throw new ObjectDisposedException("DisposeExample");

}

// Execute method functionality.

// . . .

}

}

// A class to demonstrate the use of DisposeExample.

public class Recipe13_06

{



650



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



public static void Main()

{

// The using statement ensures the Dispose method is called

// even if an exception occurs.

using (DisposeExample d = new DisposeExample())

{

// Do something with d.

}

// Wait to continue.

Console.WriteLine(Environment.NewLine);

Console.WriteLine("Main method complete. Press Enter");

Console.ReadLine();

}

}

}



13-7. Implement a Formattable Type

Problem

You need to implement a type that can create different string representations of its content based on the

use of format specifiers, for use in formatted strings.



Solution

Implement the System.IFormattable interface.



How It Works

The following code fragment demonstrates the use of format specifiers in the WriteLine method of the

System.Console class. The format specifiers are inside the braces (shown in bold in the example).

double a = 345678.5678;

uint b = 12000;

byte c = 254;

Console.WriteLine("a = {0}, b = {1}, and c = {2}", a, b, c);

Console.WriteLine("a = {0:c0}, b = {1:n4}, and c = {2,10:x5}", a, b, c);



651



www.it-ebooks.info



CHAPTER 13 ■ COMMONLY USED INTERFACES AND PATTERNS



When run on a machine configured with English (UK) regional settings, this code will result in the

output shown here:

a = 345678.5678, b = 12000, and c = 254

a = £345,679, b = 12,000.0000, and c =



000fe



As you can see, changing the contents of the format specifiers changes the format of the output

significantly, even though the data has not changed. To enable support for format specifiers in your own

types, you must implement the IFormattable interface. IFormattable declares a single method named

ToString with the following signature:

string ToString(string format, IFormatProvider formatProvider);

The format argument is a System.String containing a format string. The format string is the portion

of the format specifier that follows the colon. For example, in the format specifier {2,10:x5} used in the

previous example, x5 is the format string. The format string contains the instructions that the

IFormattable instance should use when it’s generating the string representation of its content. The .NET

Framework documentation for IFormattable states that types that implement IFormattable must

support the G (general) format string, but that the other supported format strings depend on the

implementation. The format argument will be null if the format specifier does not include a format

string component; for example, {0} or {1,20}.

The formatProvider argument is a reference to an instance of a type that implements

System.IFormatProvider, and that provides access to information about the cultural and regional

preferences to use when generating the string representation of the IFormattable object. This

information includes data such as the appropriate currency symbol or number of decimal places to use.

By default, formatProvider is null, which means you should use the current thread’s regional and

cultural settings, available through the static method CurrentCulture of the

System.Globalization.CultureInfo class. Some methods that generate formatted strings, such as

String.Format, allow you to specify an alternative IFormatProvider to use such as CultureInfo,

DateTimeFormatInfo, or NumberFormatInfo.

The .NET Framework uses IFormattable primarily to support the formatting of value types, but it

can be used to good effect with any type.



The Code

The following example contains a class named Person that implements the IFormattable interface. The

Person class contains the title and names of a person and will render the person’s name in different

formats depending on the format strings provided. The Person class does not make use of regional and

cultural settings provided by the formatProvider argument. The Main method demonstrates how to use

the formatting capabilities of the Person class.

using System;

namespace Apress.VisualCSharpRecipes.Chapter13

{

public class Person : IFormattable

{



652



www.it-ebooks.info



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

13-6. Implement a Disposable Class

Tải bản đầy đủ ngay(0 tr)

×