什么是内部异常?

33
我已经阅读了 MSDN,但是我无法理解这个概念。
如果我错了,请您纠正:
内部异常将与当前异常一起使用。 内部异常会先发生,然后是当前异常(如果存在异常),这就是为什么需要检查 InnerException 是否为 null。为了保留内部异常,我们必须将其作为参数传递。
我理解得对吗?

所以你通常只对内部异常感兴趣,但请注意,它也可能有其内部异常。您可能需要编写自己的例程来查找最内部的异常。 - Roland
也许需要获取根异常,即可能存在多个内部异常... while (ex.InnerException != null) ex = ex.InnerException; - Paul Zahra
对于那些偶然遇到这个问题的人,Kudvenkat老师有一个非常好的教程视频:https://www.youtube.com/watch?v=MO3sOTWfZPc - ivpavici
一个异常中的异常...我喜欢称它为"InCeption"。 - Jessica
3个回答

47
你可以在下面看到代码。
第一步,我将"abc"解析为int。这会引发FormatException异常。
在处理引发的FormatException异常的第一个catch块中,我尝试打开一个文本文件来记录异常消息。但是这个文件不存在,所以会引发第二个异常,这次是FileNotFoundException类型的异常。
我想要记录第二个异常的原因,所以我将第一个异常(类型为FormatException的fe)添加到第二个异常中,使用第二个异常的FileNotFoundException(String, Exception)构造函数中传入参数类型为FormatException的fe的fe参数。
该构造函数将把第一个异常存储在第二个异常的InnerException属性中。
在最外面的catch块中,我可以访问InnerException属性来知道第一个异常是什么。
这有用吗?
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}");
        }        
    }
}

控制台输出

Inner Exception:
    Input string was not in a correct format.
Outer Exception:
    NestedExceptionMessage: File `DoesNotExist` not found.

内部异常指的是最终抛出的更深层嵌套的异常。外部异常指的是范围最浅的异常。

4
外部怎么可能深,内部却浅?难道不应该相反吗? - Delfic
1
@Delfic 的确,我修复了那部分并详细阐述了答案,并添加了一些文档链接。 - Jeroen Wiert Pluimers

20

内部异常是导致当前异常的异常。

在某些情况下,您想要暴露与您的代码捕获不同的异常,但又不想放弃原始上下文时使用它。

为了让新异常具有有关先前异常的信息,就像您所说的那样,您将其作为构造函数参数传递给新异常。

通常,null内部异常意味着当前异常是异常情况的根本原因。


2

在进入catch块时,异常对象已经是只读的了。有时您的代码无法处理异常,但可以通过创建新异常并将原始抛出的异常包装在其中来添加更多信息。这样可以添加信息,而不必逐个字段地复制原始异常中的每个信息(如果您不知道将抛出哪种类型的异常,则可能无法复制所有信息)。

以下是我的一个项目中稍作修改后使用异常处理的示例代码片段。

    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; }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接