The throw
-keyword in C# generates (or translates) exceptions. By using a throw
-statement inside a catch block, we can change the resulting exception.
We can throw a new exception. The throw
-statement is versatile—throw is a statement, but also a special kind of expression.
We first look at 3 methods A, B, and C that use the throw
-statement in different ways. Method A uses a throw0statement with no argument.
throw
-statement in this way to implement custom error conditions.using System; class Program { static void Main() { // Call method A, method B, and method C. // ... Comment out the first 1-2 method invocations. try { A(); B(); C(null); } catch (Exception ex) { Console.WriteLine(ex); } } static void A() { // Rethrow syntax. try { int value = 1 / int.Parse("0"); } catch { throw; } } static void B() { // Filtering exception types. try { int value = 1 / int.Parse("0"); } catch (DivideByZeroException ex) { throw ex; } } static void C(string value) { // Generate new exception. if (value == null) { throw new ArgumentNullException("value"); } } }System.DivideByZeroException: Attempted to divide by zero. System.DivideByZeroException: Attempted to divide by zero. System.ArgumentNullException: Value cannot be null. Parameter name: value
Next we examine rethrows in the C# language. A rethrow must use a throw
-statement with no argument. If you use throw ex, then the TargetSite
and StackTrace
are changed.
X()
uses a rethrow statement. And Y()
uses a throw ex statement.TargetSite
is in StringToNumber
—an internal method of int.Parse
.Y()
, the exception's TargetSite
was modified to the current method Y()
.using System; class Program { static void Main() { try { X(); } catch (Exception ex) { Console.WriteLine(ex.TargetSite); } try { Y(); } catch (Exception ex) { Console.WriteLine(ex.TargetSite); } } static void X() { try { int.Parse("?"); } catch (Exception) { throw; // [Rethrow construct] } } static void Y() { try { int.Parse("?"); } catch (Exception ex) { throw ex; // [Throw captured ex variable] } } }Void StringToNumber(System.String, ...) Void Y()
Throw is usually used as a statement. But in some places, it can be used as an expression. We can use throw as part of a null
-coalescing statement.
Test()
method. If its argument is null
, a new ArgumentException
is thrown as part of the null
-coalescing statement.using System; class Program { static void Test(string argument) { // Use null coalescing operator with throw expression. // ... If argument is null, throw. var value = argument ?? throw new ArgumentException("Bad argument"); Console.WriteLine("HERE"); } static void Main() { Test("bird"); Test(null); } }HERE Unhandled Exception: System.ArgumentException: Bad argument at Program.Test(String argument) in c:\users\...Program.cs:line 9 at Program.Main() in c:\users\...Program.cs:line 16
A throw
-expression can only be used in certain places in programs—not everywhere. We can use a throw
-expression as part of a ternary statement.
string
's length is tested. If it has fewer than 10 characters, an Exception
is thrown as part of the ternary.using System; class Program { static void Main() { string test = "bird"; // Throw can be used in ternary. char tenth = test.Length >= 10 ? test[9] : throw new Exception("Not ten characters"); // Not reached. Console.WriteLine(tenth); } }Unhandled Exception: System.Exception: Not ten characters at Program.Main() in c:\users\...Program.cs:line 9
The exception handling mechanism exploits an alternative control pathway. The throw
-statement provides the ability to rethrow an exception or generate a new one.
We can use throw as a statement, or an expression in some places in our programs. Though the syntax is confusing at first, it becomes easier to use with familiarity.