The exception-handling features of C# will be familiar to the Java programmer; the exception-handling syntax is predominantly the same, although the languages differ significantly in their exception declaration requirements.
There is no requirement, and no ability, in C# for a function member to programmatically declare the exceptions it might throw. The only way to do so is in the API documentation. This has two consequences:
Java developers are accustomed to methods explicitly declaring the checked exceptions they might throw. The absence of a throws declaration means that the programmer is more reliant on API documentation than on the compiler.
Interface members cannot specify which exceptions the member implementations are expected to throw. A full and accurate implementation of an interface member can be achieved only by a disciplined developer referring to and accurately interpreting the interface documentation.
The following example demonstrates the familiar try...catch...finally exception-handling syntax, along with new catch syntax introduced by C#:
try { // code that might throw an exception } catch (ExceptionClass1 ex1) { // catch clause 1 // code to handle exceptions of class ExceptionClass1 // with access to the thrown exception named ex1 } catch (ExceptionClass2) { // catch clause 2 // code to handle exceptions of class ExceptionClass2 // but without access to the Exception instance } catch { // catch clause 3 // code to handle any Exception, but without access // to the Exception instance } finally { // code that executes no matter whether // the try block succeeds or fails }
Catch clause 1 is exactly the same as the Java syntax.
Catch clause 2 allows the programmer to catch an exception without declaring a local variable; there is no access to the exception caught.
Catch clause 3, known as a general catch clause, is used to capture any previously unhandled exception regardless of type. Because all run-time exceptions inherit from System.Exception, it is simply a shorthand form of writing the following:
try { // Some code } catch (System.Exception) { // Some exception-handling code }
Throwing exceptions is the same in C# as in Java. For example:
throw new ExceptionClass();
C# introduces a shorthand syntax for rethrowing caught exceptions: the throw statement with no parameters. This is required when using the new catch syntax described in the preceding section. For example:
try { // Some code } catch { // Some exception handling code // Rethrow the caught exception throw; }
The java.lang.Throwable class is the root of the Java exception class hierarchy. Two classes derive directly from Throwable: java.lang.Exception and java.lang.Error. Derived from Exception is a class named java.lang.RuntimeException. The Exception, Error, and RuntimeException subclasses of Throwable are central to the Java classification of exceptions into the checked and unchecked categories.
Unchecked exceptions are those derived from either the Error or RuntimeException class. The compiler makes no attempt to ensure that these exceptions are handled by the developer since they generally occur because of a run-time or program error that should be fixed.
Checked exceptions are those that the compiler ensures are caught and handled within the code or are declared in the throws clause of the method declaration. Checked exceptions include all exceptions derived from java.lang.Exception, excluding RuntimeException and its subclasses.
Because C# function members cannot declare exceptions, the compiler makes no attempt to ensure that exceptions are caught and handled correctly. All .NET exceptions can be considered to be unchecked exceptions in Java terminology.
.NET takes a simpler approach to classifying exceptions based on whether it is a system or an application-related exception. This is merely a convention and is not enforced by the compiler or the runtime.
The root of the exception hierarchy in .NET is System.Exception. System exceptions are represented by System.SystemException and application exceptions by System.ApplicationException, both of which are derived from System.Exception.
Table 6-1 compares the functionality of .NET’s System.Exception class with that of the Java java.lang.Throwable class. Although not a part of either language specification, the use of exceptions is tightly integrated with both Java and C# and is appropriate to this chapter. Java and .NET both rely on the exception inheritance hierarchy as a classification mechanism to support the use of the catch clause.
Both .NET and Java version 1.4 support a standardized approach to exception chaining. This allows exceptions to be created and store a reference to another exception internally. The inner exception would usually be a reference to an exception that prompted the outer exception to be raised.
Table 6-1. Comparison of Java and C# Exception Members
C# | Comments | |
---|---|---|
Constructors | ||
Throwable() | Exception() | Constructs a new exception instance. |
Throwable(String) | Exception(string) | Constructs an exception with the specified message. |
Throwable(String, Throwable) | Exception(string, Exception) | Constructs an exception with the specified message and chained exception. |
Throwable(Throwable) | N/A | Not supported in .NET; use the previous constructor. |
N/A | Exception(SerializationInfo, StreamingContext) | Constructs an exception from a previously serialized exception. See GetObjectData near the end of this table. |
Member | ||
fillInStackTrace() | N/A | Not supported. (Although Environment.StackTrace can be used if only a stack trace is needed.) |
getCause() | InnerException | Gets the chained exception, or null if one has not been specified. |
getLocalizedMessage() | N/A | .NET recommends that the message available through the Message property be localized. Java provides this second method, which should be overridden by deriving classes to return a localized version of the message. |
getMessage() | Message | Gets a message that describes the exception. |
getStackTrace() | StackTrace | Gets the stack trace from the exception as a string. |
initCause() | N/A | Sets the chained exception reference. .NET allows this reference to be set only during construction. |
printStackTrace() | N/A | Not supported. Use the ToString method followed by the appropriate Stream or Console method. |
setStackTrace() | N/A | Not supported. |
HelpLink | Gets or sets a link to related help information. Should be in the form of a URN or URL. | |
N/A | Source | Gets or sets the name of the application or object that caused the exception. By default, this will be the assembly name. |
N/A | TargetSite | Gets a MethodBase class representing the method that threw the exception. |
N/A | GetBaseException() | Returns the original exception at the root of the exception chain. |
N/A | GetObjectData() | Serializes an exception. |
N/A | HResult | Gets or sets a coded number that uniquely identifies an exception. Predominantly for legacy integration. |