Anda di halaman 1dari 40

Chapter - 5

Exceptions and Object Lifetime

Chapter Objectives

What is an Exception? basics System.Exception Class Catching an Exception System Level Exceptions Building Customized Exceptions Multiple Exceptions Garbage Collection - basics What is "finally"? Building Finallizable and Disposable Types

Introduction

An exception is any error condition or unexpected behavior encountered by an executing program. These are run time anomalies. In the .NET Framework, an exception is an object that inherits from the System.Exception class An exception is thrown from an area of code where a problem has occurred .NET base class libraries define: OutOfMemoryException, IndexOutOfRangeException, FileNotFoundException, and so on

.NET Exceptions

A type that represents the details of the exceptional circumstance A method that throws the exception to the caller A block of code that will invoke the exception-ready method

A block of code that will process or catch the exception

.NET Exceptions

The CLR supports an exception handling model based on the concepts of exception objects and protected blocks of code The runtime creates an object to represent an exception when it occurs. You can also create your own exception classes by deriving classes from the appropriate base exception Each language uses a form of try/catch/finally structured exception handling

Why Exceptions?

Code that uses exceptions are clearer and more robust than other methods
bool RemoveFromAccount(string AcctID, decimal Amount) { bool Exists = VerifyAmount(AcctID); if (Exists) { bool CanWithDraw = CheckAv(AcctID, Amount); if (CanWithDraw) { return WithDraw(AcctID, Amount); } } return false; }

Disadvantages of Traditional Methods

Code that depends on return value must provide additional error parameters or call other functions (e.g. Win32 API) Returning error code must be handled properly Error codes are difficult to extend and update. The entire source code must be updated for any new error codes C# uses rich Exception object which contains info about failure condition

Handling Exceptions

To handle an exception you must protect with a try block and have at least one catch block
try { Employee e; e.GiveBonus(); // null reference } catch { // Handle any exception }

How Exceptions are Executed?

To catch an exception the try-catch block must be used A try block is a section of the code that looks for an exception When an exception is raised, or thrown, within a try block, an exception object is created and a search begins in the catch block When an exception occurs within that scope, the control goes to the appropriate catch block (handler) If no exception occurs, obviously the catch block is never executed After the catch block code is executed the rest of the code in the program continues (is it OK?) Catch block can also rethrow the exception

Catching Specific Exceptions


try {

} catch (NullReferenceException ex) { // Log the error throw(ex); }

Employee e; e.GiveBonus(); // null reference

If the catch block can recover from the error that caused the exception, execution can continue after catch block. If it can't, then it must rethrow (shown in red color). If an exception is rethrown, the next catch block will be checked. The check may continue in the call stack until a proper handler is found. If none found, terminate!

throw statement

The throw statement is used to signal the occurrence of an anomalous situation /exception and to send the error object back to the caller (raising an exception) throw [expression]; expression is an exception object Usually the throw statement is used with try-catch or try-finally statements. When an exception is thrown, the program looks for the catch statement that handles this exception

Example (throw)
using System; public class ThrowTest { public static void Main() { Output: The following exception occurs: string s = null; System.ArgumentNullException if (s == null) { throw(new ArgumentNullException()); } // not executed Console.Write("The string s is null"); } }

Example (try - catch)


using System; class AddInts { static void Main(string[] args) { int a, b; int c, d = 0; Console.Write("Enter the first number (a): "); a = System.Int32.Parse(Console.ReadLine()); Console.Write("Enter the second number (b): "); b = System.Int32.Parse(Console.ReadLine()); c = a + b;

try {

} catch(Exception e) { Console.WriteLine("Method: {0}", e.TargetSite);


Console.WriteLine("Message: {0}", e.Message);

d = a / b;

} Console.WriteLine("a + b = {0}", c); Console.WriteLine("a / b = {0}", d);

Console.WriteLine("Source: {0}", e.Source); return;

Enter the first number (a): 28 Enter the second number (b): 0 Method: Void Main(System.String[]) //which method threw the exception Message: Attempted to divide by zero. Source: AddInts

System.Exception Base Class

Most of the memebers defined by System.Exception are read-only


Property
HelpLink InnerException Message Source

Meaning
URL to a help file details of previous exceptions that caused the current exception returns the text describing the error name of the assembly that threw the exception

StackTrace
TargetSite

sequence of calls that triggered the exception


returns the MethodBase type

Example-2 : StackTrace

StackTrace: Displays sequence of calls that resulted in throwing the exception. We can follow the flow of the error's origin
catch(Exception e) { Console.WriteLine("Stack: {0}",e.StackTrace); }

Example-3 : HelpLink
try { // create a new Exception object, passing a string Exception myException = new Exception("myException"); // set the HelpLink and Source properties myException.HelpLink = "See the Readme.txt file"; myException.Source = "My HelpLink Program"; // throw the Exception object throw myException;

} catch (Exception e) { // display the exception object's properties Console.WriteLine("HelpLink = " + e.HelpLink); Console.WriteLine("Source = " + e.Source);
}

HelpLink = See the Readme.txt file Source = My HelpLink

Building Custom Exceptions

Though the System level exceptions are useful, sometimes it is necessary to build user defined exceptions Define a new user defined class derived from System.Exception You may override any virtual members defined in the parent class

Square Root
using System; namespace CustomException { class NegNumException : ApplicationException { class NegNumException : ApplicationException { public NegNumException() : base("SNG: Negative Number Exception") {} public NegNumException(string msg) : base(msg) {} public NegNumException(string msg, Exception inner) : base(msg, inner) {} }

Square Root
class CustomException { public double SqRoot(double n) { if (n < 0) // calls 1-arg ctor. //throw new NegNumException ("SNG: Negative Number not allowed"); // calls default ctor. throw new NegNumException(); return Math.Sqrt(n); }

Square Root
public void SqRootTest() { double n; try { Console.Write("Enter a number: "); n = System.Double.Parse(Console.ReadLine()); double result = SqRoot(n); Console.WriteLine("Sq Root = {0}", result); } catch(FormatException ex) { Console.WriteLine(ex.Message); } catch(NegNumException ex) { Console.WriteLine(ex.Message); } }

Square Root
static void Main(string[] args) { CustomException e = new CustomException(); e.SqRootTest(); } }

Multiple Exceptions

The code in the try block may trigger more than one exception. Under these circumstances you can have more than one catch block Exception will be processed by the nearest available catch block (Below example is wrong!) Ex: try { }
catch (Exception e) {} catch (NegNumException e) {} catch (ArgumentOutOfRangeException e) {}

Unreachable

Other types of Exception Handling

Generic catch try {} catch {}

No argument

Rethrowing Exceptions try {} catch (NegNumException e) { // handle the error and pass the buck throw e; rethrow the same }
exception

The finally Block - The optional finally block will get executed always - Useful for cleaning up process or close the database files

Garbage Collection

C# Programmers never directly deallocate an object from memory so no 'delete' keyword Rule: Allocate an object on to a managed heap using 'new' and forget about it! How does .NET determine that an object is no longer needed? When it is unreachable by the current application Example:
public static int Main(string[] args) { When the Car c = new Car( ); application shuts . down, object c may

be destroyed.

Garbage Collector

CIL emits newobj for new .NET garbage collector maintains a new object pointer which indicates where the next object should be placed in the heap Assume c1, c2, and c3 are objects of type Car c3 c2 c1 Unused heap
next object pointer

Garbage Collector

If the managed heap does not have enough memory, CLR does garbage collection. It frees the memory to accommodate the new object Which object memory is to be removed "no longer needed" basis Objects that have been recently allocated are most likely to be freed Objects that have lived the longest are least likely to be become free Objects allocated together will be used together To get this idea CLR uses an object graph to know whether the objects are still in use. The other objects which are no longer used "severed roots" or unreachable objects are reclaimed and the memory is compacted with necessary object pointer adjustment

Garbage Collector

The Garbage Collector in .NET is known as generational garbage collector. Allocated objects are sorted into three groups or generations: generation 0: objects allocated most recently generation 1: Objects that survive a garbage collection pass are moved into generation 1 generation 2: Objects that survive in generation 1 are moved into generation 2 If no memory is available after compaction, an OutOfMemoryException is thrown

Finalizing a Type

The garbage collection process is nondeterministic To release the memory in a timely manner, one possible solution is to override System.Object.Finalize() method. We go for this only when C# types are using unmanaged types (Win32 APIs) Unmanaged resources: database connections and GDI Note that you can't override this method directly: public class FinalizedCar { protected override void Finalize() { } // Wrong } protected void Finalize() // don't expose this method { base.Finalize(); } Do not define both a destructor and Finalize method in the same class

Indirectly Invoking Finalize()

When an Application domain is unloaded by CLR, method is invoked for all finalizable objects class FinalizedCar { ~FinalizedCar() { Console.WriteLine("Finalizing"); } class FinalizeApp { static void Main(string[] args) { Console.WriteLine("Object creation"); FinalizedCar fc = new FinalizedCar(); Console.WriteLine("Exiting Main"); } } }

the Finalize()

// 3

// 1 // 2

Instead of using a finalizer (because it is costly), you can expose the Dispose() method

Dispose with using

With the presence of the using statement, which opens a scope at the end of which a given object will be disposed, regardless of whether the scope is exited normally or via an exception.
using( Pen myPen = new Pen(Color.Red) ) { // Do stuff with myPen }

When objects are of the same type, you may put them all inside the same using statement:
using( Pen myRedPen = new Pen(Color.Red), myBluePen = new Pen(Color.Blue) ) { // Do stuff with both pens }

Dispose with using

For instance, you'll need a Font and a Brush if you want to use the Graphics.DrawString method to paint some text. Unfortunately, here's where the using statement starts to break down, because the following code generates a compile error:
using( Font myFont = new Font("Arial", 16), SolidBrush myBrush = new SolidBrush(Color.Blue) ) { // COMPILE ERROR! }

That snippet doesn't compile because C# doesn't let you have more than one type in a single using statement. So, your next alternative is to nest using statements like this:
using( Font myFont = new Font("Arial", 16) ) { using( SolidBrush myBrush = new SolidBrush(Color.Blue) ) { { // Do stuff with a font and a brush } } }

Dispose with using

But use of more than a couple of levels of nesting, and your code gets ugly pretty quickly Hence, we must search for a better solution

Implementing IDisposable

Creating and properly disposing of an object requires several lines of correctly written code. C# provides an automated solution based on IDispose interface IDispose interface provided in .NET is for symmetry among all objects that supports an explicit destruction

IDispose Implementation Flow

Implementing IDisposable
public class MyClass : IDisposable { public void Dispose() { Console.WriteLine("Custom cleaning process"); } } public class IDisposeApp { public static void Main(string[] args) { MyClass m = new MyClass(); m.Dispose(); } }

Example
public class ClassBeingTested : IDisposable { private bool disposed=false; private Image img=null; public Image Image { get {return img;} } // the constructor public ClassBeingTested() { Trace.WriteLine("ClassBeingTested: Constructor"); } // the destructor ~ClassBeingTested() { Trace.WriteLine("ClassBeingTested: Destructor"); // call Dispose with false. Since we're in the // destructor call, the managed resources will be // disposed of anyways. Dispose(false); }

public void Dispose() Trace.WriteLine("ClassBeingTested: Dispose"); // dispose of the managed and unmanaged resources Dispose(true); // tell the GC that the Finalize process no longer needs // to be run for this object. GC.SuppressFinalize(this);

protected virtual void Dispose(bool disposeManagedResources) {

// process only if mananged and unmanaged resources have not been disposed of. if (!this.disposed) { Trace.WriteLine("ClassBeingTested: Resources not disposed"); if (disposeManagedResources) { Trace.WriteLine("ClassBeingTested: Disposing managed resources"); // dispose managed resources if (img != null) { img.Dispose(); img=null; } } // dispose unmanaged resources Trace.WriteLine("ClassBeingTested: Disposing unmanaged resouces"); disposed=true; } else { Trace.WriteLine("ClassBeingTested: Resources already disposed"); }

End of Chapter - 5

For more details on IDispose implementation, please refer to:


www.codeproject.com/csharp/IDispose.asp C# How to Program by DEITEL & DEITEL

Anda mungkin juga menyukai