سؤال

I have read the MSDN but, I could not understand this concept.

Correct me if I am wrong,

A innerexception will be used in hand with current exception.

Inner exception will occur first and then the current exception will occur (if there is an exception) that is the reason why InnerException is checked against null. In order to retain inner exception we have to pass it as a parameter.

Am I right with this?

هل كانت مفيدة؟

المحلول

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.

نصائح أخرى

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.

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; }
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top