© 2006, Flemming Koch Jensen
Alle rettigheder forbeholdt
Exceptions

 

 

Exceptions i C# er i store træk som i Java. Alle exceptions er instanser af en klasse der nedarver fra System.Exception.

 

1. try-catch-finally

catch sukker try- og finally-blokke fungerer på nøjagtig samme måde som i Java. Mht. catch-blokke er der lidt syntaktisk sukker som vi vil se på i det følgende.
Man kan som i Java have flere catch-blokke, og der vælges på samme måde den catch-blok der først passer. Hvis man i catch-blokken ikke er interesseret i den konkrete exception, kan man udelade den formelle parameters navn. F.eks.:
try {
  int tæller = 5;
  int nævner = 0;

  int x = tæller / nævner;
}
catch ( DivideByZeroException ) {
  Console.WriteLine( "Division med 0" );
}
Division med 0
Her anvender vi ikke den exception, der bliver kastet, til andet end at styre hvilken catch-blok der bliver udført.
Hvis man vil gribe enhver exception anfører man naturligvis blot Exception, men er man (som ovenfor) ikke interesseret i Exception-objektet, kan man forkorte det til:
try {
  int tæller = 5;
  int nævner = 0;

  int x = tæller / nævner;
}
catch {
  Console.WriteLine( "Noget gik galt" );
}
Noget gik galt

 

2. Metode-kald

Ingen throws Ligesom i Java, vil en exception der ikke bliver håndteret i en metode, blive kastet tilbage til, hvor metoden er blevet kaldt fra. I C# er alle exceptions run-time exceptions, og man skriver derfor aldrig throws... efter metodens signatur, som man ellers kender det fra Java.
Hvis man ikke håndterer en exception i sit program, og den dermed bliver kastet videre fra Main, får man en dialogbox:
samt en fejlmeddelelse:
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
   at Test.Main() in C:\Projects\C#\Test\Test.cs:line 5
Hvis man griber en exception, og dernæst ønsker at kaste den, skriver man blot throw. F.eks.:
try {
  int tæller = 5;
  int nævner = 0;
    
  int x = tæller / nævner;
}
catch ( DivideByZeroException e ) {
  Console.WriteLine( e );
      
  throw;
}
Man bemærker at det er underforstået, at den exception der kastes, er den catch-blokken greb.

 

3. Wrappe exceptions

Ekstra information Når f.eks. en catch-blok vælger at kaste en exception tilbage, til hvor metoden er blevet kaldt fra, har man mulighed for at wrappe den i en ny exception, der kan tilføjes ekstra information. F.eks.:
using System;

public class Test {

  private static void M() {
    try {
      int tæller = 5;
      int nævner = 0;
    
      int x = tæller / nævner;
    }
    catch ( DivideByZeroException e ) {
      throw new DivideByZeroException( "'nævner' er 0", e );
    }
  }

  public static void Main() {
    try {
      M();
    }
    catch ( DivideByZeroException e ) {
      Console.WriteLine( e );
    } 
  }
}
System.DivideByZeroException: 'nævner' er 0 ---> System.DivideByZeroException:
Attempted to divide by zero.
   at Test.M() in C:\Projects\C#\Test\Test.cs:line 10
   at Test.M() in C:\Projects\C#\Test\Test.cs:line 13
   at Test.Main() in C:\Projects\C#\Test\Test.cs:line 19
Man ser af udskriften, at de to exceptions bliver listet efter hinanden. Den vi selv har lavet:
System.DivideByZeroException: 'nævner' er 0
Og den vi oprindelig greb:
System.DivideByZeroException: Attempted to divide by zero.
Selv om vi har valgt det i dette eksempel, behøver de to exceptions ikke nødvendigvis være af samme klasse

 

4. Egne exceptions

Såfremt man laver sine egne exceptions skal de nedarve fra System.ApplicationException. F.eks.:
using System;

class DivisionsException: ApplicationException {
  int tæller;
  
  public DivisionsException( int tæller ) {
    this.tæller = tæller;
  }
  
  public DivisionsException( string message, int tæller ): base( message ) {
    this.tæller = tæller;
  } 
  
  public DivisionsException(
    string message, Exception e, int tæller ): base( message, e )
  {
    this.tæller = tæller;
  }
  
  public int Tæller {
    get {
      return tæller;
    } 
  } 
} 

public class Test {

  private static void M() {
    int tæller = 5;
    int nævner = 0;
    
    try {
      int x = tæller / nævner;
    }
    catch ( DivideByZeroException e ) {
      throw new DivisionsException( "'nævner' er 0", e, tæller );
    }
  }

  public static void Main() {
    try {
      M();
    }
    catch ( DivisionsException e ) {
      Console.WriteLine( "Division fejlede: {0}/0", e.Tæller );
    } 
  }
}
Division fejlede: 5/0
Man bør i den forbindelse lave de to konstruktorer - den der tager en besked, og den anden der tillige tager en exception. Med dem kan man instantiere ens egne exceptions på samme måde som systemets (f.eks. når vi wrapper exceptions).

 

5. System.Exception

Exception-klassen har følgende tre properties som kan være nyttige (de er alle kun get-properties):
Message
InnerException
StackTrace
Message giver den tekstuelle beskrivelse der er tilknyttet. F.eks.: "'nævner' er 0", som vi har brugt ovenfor.
InnerException giver os den exception der evt. kan være wrapped i denne.
StackTrace giver os en tekstreng (med indsatte linieskift), der beskriver stien til det sted hvor den er blevet kastet. F.eks.:
at Test.M() in C:\Projects\C#\Test\Test.cs:line 13
at Test.Main() in C:\Projects\C#\Test\Test.cs:line 19
Man bemærker at Exception's ToString bruger de oplysninger vi kan få fra disse tre properties.