What is inner Exception
You can see the code below.
First step, I parse "abc"
to int
. It will raise FormatException
.
In the very first catch
block (that handles the raised FormatException
), I try to open a text file to log the exception message. But this file does not exist so a second exception is raised, this time of type FileNotFoundException
.
I want to store what caused the second exception to be raised, so I add the first exception (fe
of type FormatException
) to the with the fe
parameter (of type FormatException
) passed in the FileNotFoundException(String, Exception)
constructor of the second exception.
That constructor will store the first exception in the InnerException
property of the second exception.
In the outermost catch
block, I can access InnerException
's properties to know what the first exception is.
Is it useful?
using System;
using System.IO;
public class Program
{
public static void Main( )
{
try
{
try
{
var num = int.Parse("abc"); // Throws FormatException
}
catch ( FormatException fe )
{
try
{
var openLog = File.Open("DoesNotExist", FileMode.Open);
}
catch
{
throw new FileNotFoundException("NestedExceptionMessage: File `DoesNotExist` not found.", fe );
}
}
}
// Consider what exception is thrown: FormatException or FileNotFoundException?
catch ( FormatException fe )
{
// FormatException isn't caught because it's stored "inside" the FileNotFoundException
}
catch ( FileNotFoundException fnfe )
{
string innerMessage = "", outerMesage;
if (fnfe.InnerException != null)
innerMessage = fnfe.InnerException.Message; // Inner exception (FormatException) message
outerMesage = fnfe.Message;
Console.WriteLine($"Inner Exception:\n\t{innerMessage}");
Console.WriteLine($"Outer Exception:\n\t{outerMesage}");
}
}
}
Console Output
Inner Exception:
Input string was not in a correct format.
Outer Exception:
NestedExceptionMessage: File `DoesNotExist` not found.
The Inner exception refers to the deeper nested exception that is ultimately thrown. The Outer exception refers the most shallow (in scope) exception.
Exception objects are read only by the time you get to a catch
block, sometimes your code can't do anything to handle the exception, but it can add more information by creating a new exception and wrapping the originally thrown exception inside of it. This makes it so you can add information but not be required to copy field by field every piece of information from the original exception (which may not even be possible if you don't know the type of exception that will be thrown).
Here is a slightly modified snippet from a project of mine that uses a bit of everything dealing with Exceptions.
private void SomeFunction(string username, string password)
{
try
{
try
{
_someObject.DoSpecialPrivilegedFunction(username, password);
}
catch (UnauthorizedAccessException ex)
{
throw new UserUnauthorizedException(username, "DoSpecialPrivilegedFunction", ex);
}
catch (IOException ex)
{
throw new UserModuleActionException("A network IO error happend.", username, "DoSpecialPrivilegedFunction", ex);
}
//Other modules
}
catch (Exception ex)
{
//If it is one of our custom expections, just re-throw the exception.
if (ex is UserActionException)
throw;
else
throw new UserActionException("A unknown error due to a user action happened.", username, ex);
}
}
//elsewhere
[Serializable]
public class UserUnauthorizedException : UserModuleActionException
{
private const string DefaultMessage = "The user attempted to use a non authorized module";
public UserUnauthorizedException()
: base(DefaultMessage)
{
}
public UserUnauthorizedException(string message)
: base(message)
{
}
public UserUnauthorizedException(string message, Exception innerException)
: base(message, innerException)
{
}
public UserUnauthorizedException(string username, string module, Exception innerException = null) : base(DefaultMessage, username, module, innerException)
{
}
protected UserUnauthorizedException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
[Serializable]
public class UserModuleActionException : UserActionException
{
private readonly string _module;
public UserModuleActionException()
{
}
public UserModuleActionException(string message) : base(message)
{
}
public UserModuleActionException(string message, Exception innerException) : base(message, innerException)
{
}
public UserModuleActionException(string message, string username, string module, Exception innerException = null)
: base(message, username, innerException)
{
_module = module;
}
protected UserModuleActionException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
public virtual string Module
{
get { return _module; }
}
public override string Message
{
get
{
string s = base.Message;
if (!String.IsNullOrEmpty(_module))
{
return s + Environment.NewLine + String.Format("Module: {0}", _module);
}
return base.Message;
}
}
}
[Serializable]
public class UserActionException : Exception
{
private readonly string _username;
public UserActionException()
{
}
public UserActionException(string message)
: base(message)
{
}
public UserActionException(string message, Exception innerException)
: base(message, innerException)
{
}
public UserActionException(string message, string username, Exception innerException = null)
: base(message, innerException)
{
_username = username;
}
protected UserActionException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
public override string Message
{
get
{
string s = base.Message;
if (!String.IsNullOrEmpty(_username))
{
return s + Environment.NewLine + String.Format("Username: {0}", _username);
}
return base.Message;
}
}
public virtual string Username
{
get { return _username; }
}
}
An inner exception is the exception that caused the current exception.
It is used in cases when you want to surface a different exception than the one that your code caught but you don't want to throw away the original context.
In order for a new exception to have information about a previous one, like you said, you pass it as constructor parameter to the new one.
Usually, a null inner exception means that the current exception is root cause of the exceptional situation.