본문 바로가기

Software/C#.Net

Understanding Structured Exception Handling

Terms of Software problem
 Bugs: These are, simply put, errors made by the programmer. For example, suppose you are programming with unmanaged C++. If you fail to delete dynamically allocated memory, resulting in a memory leak, you have a bug. 
 
User errors: User errors, on the other hand, are typically caused by the individual running your application, rather than by those who created it. For example, an end user who enters a malformed string into a text box could very well generate an error if you fail to handle this faulty input in your code base. 
 
Exceptions: Exceptions are typically regarded as runtime anomalies that are difficult, if not impossible, to account for while programming your application. Possible exceptions include attempting to connect to a database that no longer 
exists, opening a corrupted XML file, or trying to contact a machine that is currently offline. In each of these cases, the programmer (or end user) has little control over these “exceptional” circumstances. 

System.Exception is base class of all user defined exceptions


Core Members of the System.Exception Type  
 public virtual IDictionary Data { get; } This read-only property retrieves a collection of key/value pairs (represented by an object implementing IDictionary) that provide additional, programmer-defined information about the exception. By default, this collection is empty
public virtual string HelpLink { get; set; }  This property gets or sets a URL to a help file or web site describing the error in full detail.
public Exception InnerException { get; }  This read-only property can be used to obtain information about the previous exception(s) that caused the current exception to occur. The previous exception(s) are recorded by passing them into the constructor of the most current exception.
public virtual string Message { get; }  This read-only property returns the textual description of a given error. The error message itself is set as a constructor parameter.
public virtual string Source { get; set; This property gets or sets the name of the assembly, or the object, that threw the current exception.
public virtual string StackTrace { get; }  This read-only property contains a string that identifies the sequence of calls that triggered the exception. As you might guess, this property is very useful during debugging or if you wish to dump the error to an external error log.
public MethodBase TargetSite { get; }  This read-only property returns a MethodBase object, which describes numerous details about the method that threw the exception (invoking ToString() will identify the method by name).
   


Create Exception to throw it
 if (CurrentSpeed >= MaxSpeed) 
    { 
      carIsDead = true; 
      CurrentSpeed = 0; 
 
      // We need to call the HelpLink property, thus we need 
      // to create a local variable before throwing the Exception object. 
      Exception ex = 
        new Exception(string.Format("{0} has overheated!", PetName)); 
      ex.HelpLink = "http://www.CarsRUs.com"; 
 
      // Stuff in custom data regarding the error. 
      ex.Data.Add("TimeStamp", 
        string.Format("The car exploded at {0}", DateTime.Now)); 
      ex.Data.Add("Cause", "You have a lead foot."); 
      throw ex; 
    } 
*** Error! *** 
Member name: Void Accelerate(Int32) 
Class defining member: SimpleException.Car 
Member type: Method 
Message: Zippy has overheated! 
Source: SimpleException 
Stack: at SimpleException.Car.Accelerate(Int32 delta) 
       at SimpleException.Program.Main(String[] args) 
Help Link: http://www.CarsRUs.com 
 
-> Custom Data: 
-> TimeStamp: The car exploded at 1/12/2010 8:02:12 PM 

System-Level Exceptions (System.SystemException) NullReferenceException is-a SystemException
public class SystemException : Exception 
{ 
  //  Various constructors. 
} 

Application-Level Exceptions (System.ApplicationException)
public class ApplicationException : Exception 
{ 
  //  Various constructors. 
} 
First, User Exception
Derived ApplicationException and override a member method
public class CarIsDeadException : ApplicationException 
{ 
  private string messageDetails = String.Empty; 
  public DateTime ErrorTimeStamp {get; set;} 
  public string CauseOfError {get; set;} 
 
  public CarIsDeadException(){} 
  public CarIsDeadException(string message, 
    string cause, DateTime time) 
  { 
    messageDetails = message; 
    CauseOfError = cause; 
    ErrorTimeStamp = time; 
  } 
 
  // Override the Exception.Message property. 
  public override string Message 
  { 
    get 
    { 
      return string.Format("Car Error Message: {0}", messageDetails); 
    } 
  } 
} 
Second User Exception
By Passing error message rather than derive
public class CarIsDeadException : ApplicationException 
{ 
  public DateTime ErrorTimeStamp { get; set; } 
  public string CauseOfError { get; set; } 
 
  public CarIsDeadException() { } 
 
  // Feed message to parent constructor. 
  public CarIsDeadException(string message, string cause, DateTime time) 
    :base(message) 
  { 
    CauseOfError = cause; 
    ErrorTimeStamp = time; 
  } 
} 
Third, Final Version :
requires of custom exception
  • Derives from ApplicationException
  • Is marked with the [System.Serializable] attribute
  • Defines a default constructor
  • Defines a constructor that sets the inherited Message property
  • Defines a constructor to handle “inner exceptions”
  • Defines a constructor to handle the serialization of your type
[Serializable] 
public class CarIsDeadException : ApplicationException 
{ 
  public CarIsDeadException() { } 
  public CarIsDeadException(string message) : base( message ) { } 
  public CarIsDeadException(string message, 
                            System.Exception inner)  
    : base( message, inner ) { } 
  protected CarIsDeadException( 
    System.Runtime.Serialization.SerializationInfo info, 
    System.Runtime.Serialization.StreamingContext context) 
    : base( info, context ) { } 
 
  // Any additional custom properties, constructors and data members... 
} 

Rethrowing Exceptions
// Passing the buck. 
static void Main(string[] args) 
{ 
... 
  try 
  { 
    // Speed up car logic... 
  } 
  catch(CarIsDeadException e) 
  { 
    // Do any partial processing of this error and pass the buck. 
    throw; 
  } 
... 
} 
Inner Exception inside catch, you try do something such as save log to file, then needs file open, but there is possibility to occur exception about File Open
inside catch block, set new try,catch block
catch (CarIsDeadException e) 
{ 
  try 
  { 
    FileStream fs = File.Open(@"C:\carErrors.txt", FileMode.Open); 
    ... 
  } 
  catch (Exception e2) 
  { 
    // Throw an exception that records the new exception, 
    // as well as the message of the first exception. 
    throw new CarIsDeadException(e.Message, e2); 
  } 
} 
multiple catch and finally block
set general catch block in the last case, and set finally block to make sure no problem your logic
static void Main(string[] args) 
{ 
  Console.WriteLine("***** Handling Multiple Exceptions *****\n"); 
  Car myCar = new Car("Rusty", 90); 
  myCar.CrankTunes(true); 
 
  try 
  { 
    // Speed up car logic. 
  } 
  catch(CarIsDeadException e) 
  { 
    // Process CarIsDeadException. 
  } 
  catch(ArgumentOutOfRangeException e) 
  { 
    // Process ArgumentOutOfRangeException. 
  } 
  catch(Exception e) 
  { 
    // Process any other Exception. 
  } 
  finally 
  { 
    // This will always occur. Exception or not. 
    myCar.CrankTunes(false); 
  } 
  Console.ReadLine(); 
}