C-Sharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript | SQL | PHP | Angular | HTML
Here: We divide by zero. Sadly this results in a DivideByZeroException. This operation cannot be continued.
DivideTry: We use the try and catch blocks to structure our error handling. This may lead to cleaner code.
TryCatchC# program that throws an exception
using System;
class Program
{
static void Main()
{
try
{
int value = 1 / int.Parse("0");
Console.WriteLine(value);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Output
Attempted to divide by zero.
HelpLink: This is empty because it was not defined on the exception. HelpLink is a string property.
Message: This is a short description of the exception's cause. Message is a read-only string property.
Source: This is the application name. Source is a string property that can be assigned to or read from.
StackTrace: This is the path through the compiled program's method hierarchy that the exception was generated from.
TargetSite: This is the name of the method where the error occurred. This property helps simplify what part of the errors are recorded.
C# program that shows exception properties
using System;
class Program
{
static void Main()
{
try
{
int value = 1 / int.Parse("0");
}
catch (Exception ex)
{
Console.WriteLine("HelpLink = {0}", ex.HelpLink);
Console.WriteLine("Message = {0}", ex.Message);
Console.WriteLine("Source = {0}", ex.Source);
Console.WriteLine("StackTrace = {0}", ex.StackTrace);
Console.WriteLine("TargetSite = {0}", ex.TargetSite);
}
}
}
Output: truncated
HelpLink =
Message = Attempted to divide by zero.
Source = ConsoleApplication1
StackTrace = at Program.Main() in C:\...\Program.cs:line 9
TargetSite = Void Main()
Here: In this example, we use a try construct. In the try block, a new exception is allocated. Next we assign to the Data property.
Tip: Data can be used as a Hashtable or Dictionary. The keys and values are represented by the object type.
Finally: The exception instance is thrown. And in the catch block we display the Data contents.
C# program that uses Data property
using System;
using System.Collections;
class Program
{
static void Main()
{
try
{
// Create new exception.
var ex = new DivideByZeroException("Message");
// Set the data dictionary.
ex.Data["Time"] = DateTime.Now;
ex.Data["Flag"] = true;
// Throw it.
throw ex;
}
catch (Exception ex)
{
// Display the exception's data dictionary.
foreach (DictionaryEntry pair in ex.Data)
{
Console.WriteLine("{0} = {1}", pair.Key, pair.Value);
}
}
}
}
Output
Time = 12/9/2014 5:38:22 PM
Flag = True
Tip: To make one, create a class. Have it derive from Exception—the class is the base class for all exceptions.
Message: You can add a public override string property, Message, to specify the string displayed by the Exception.
Caution: Custom types should be reluctantly used. They tend to add more complexity. Consider instead just using built-in ones.
Quote: In C#, all exceptions must be represented by an instance of a class type derived from System.Exception. In C++, any value of any type can be used to represent an exception (The C# Programming Language).
C# program that throws custom exception
using System;
class TestException : Exception
{
public override string Message
{
get
{
return "This exception means something bad happened";
}
}
}
class Program
{
static void Main()
{
try
{
throw new TestException();
}
catch (TestException ex)
{
Console.WriteLine(ex);
}
}
}
Output
TestException: This exception means something bad happened
at Program.Main()....
Tip: This pattern improves the performance of certain important functions by avoiding exception handling.
Further: You can use the tester-doer pattern in your own function designs. It yields similar benefits.
Speed: The Tester-Doer pattern is a clear performance win over exceptions in almost all situations.
C# program that shows a tester-doer method
using System;
class Program
{
static void Main()
{
// This is not valid!
string value = "abc";
int result;
if (int.TryParse(value, out result)) // Tester-doer method.
{
// Not reached.
// ... Result would have the valid parsed result.
Console.WriteLine(result);
}
}
}
Version 1: This version of the code handles errors by testing against the null literal in an if-statement.
Version 2: Here we use try-catch to handle errors. We throw an exception if the argument array is null.
Result: The try-catch block has a negative effect on performance. If the array is null, performance would be even worse.
C# program that shows exceptions
using System;
using System.Diagnostics;
class Program
{
static int GetA(int[] arr)
{
if (arr != null) // Check for null.
{
return arr[0];
}
else
{
return 0;
}
}
static int GetB(int[] arr)
{
try
{
return arr[0];
}
catch // Catch exceptions.
{
return 0;
}
}
const int _max = 1000000;
static void Main()
{
int[] arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int count = 0;
var s1 = Stopwatch.StartNew();
// Version 1: use if-statement to handle errors.
for (int i = 0; i < _max; i++)
{
int v = GetA(arr);
if (v == 5)
{
count++;
}
}
s1.Stop();
var s2 = Stopwatch.StartNew();
// Version 2: use try-catch to handle errors.
for (int i = 0; i < _max; i++)
{
int v = GetB(arr);
if (v == 5)
{
count++;
}
}
s2.Stop();
Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000000) /
_max).ToString("0.00 ns"));
Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000000) /
_max).ToString("0.00 ns"));
Console.Read();
}
}
Output
7.91 ns: GetA, if check
16.85 ns: GetB, try-catch
Version 1: Here the try-catch block is inside the inner loop. So we enter the protected region on each loop iteration.
Version 2: The try-catch block is outside the loop. The logic is different—if an exception is thrown, the entire loop terminates.
Result: Version 2 (with the try-catch outside the loop) is faster. Hoisting exception handling outside a loop helps.
C# program that optimizes exception construct
using System;
using System.Diagnostics;
class Program
{
const int _max = 1000000;
static void Main()
{
var s1 = Stopwatch.StartNew();
// Version 1: try-catch inside loop.
for (int i = 0; i < _max; i++)
{
Method1();
}
s1.Stop();
var s2 = Stopwatch.StartNew();
// Version 2: try-catch outside loop.
for (int i = 0; i < _max; i++)
{
Method2();
}
s2.Stop();
Console.WriteLine(((double)(s1.Elapsed.TotalMilliseconds * 1000 * 1000) /
_max).ToString("0.00 ns"));
Console.WriteLine(((double)(s2.Elapsed.TotalMilliseconds * 1000 * 1000) /
_max).ToString("0.00 ns"));
Console.Read();
}
static void Method1()
{
for (int i = 0; i < 1000; i++)
{
try
{
int value = i * 100;
if (value == -1)
{
throw new Exception();
}
}
catch
{
}
}
}
static void Method2()
{
try
{
for (int i = 0; i < 1000; i++)
{
int value = i * 100;
if (value == -1)
{
throw new Exception();
}
}
}
catch
{
}
}
}
Output
2555.43 ns: Method1
674.29 ns: Method2
But: This did not consider errors. To contain complexity, exception handling extends methods.
And: Exception handling adds another layer of control flow. We attack complexity with this new logical layer.
Quote: Exceptions in C# provide a structured, uniform, and type-safe way of handling both system-level and application-level error conditions (The C# Programming Language).