1

我们的系统有一个内部异常处理程序,可以捕获、管理、记录和通知我们异常。但有时并非所有例外情况都应被跟踪/通知/通过电子邮件发送。

例如,如果在方法/类中发生异常,我希望异常处理程序能够识别并能够以不同的方式处理该异常。

最初我尝试使用可继承的属性来实现它,但如果该属性位于堆栈跟踪中更深的某个方法/类上,它显然不会被拾取。

由于我认为我不能用属性来实现它(对吗?),我正在考虑将特定代码块包装在 using() 块中,但我不知道如何在异常处理程序中检查异常是否发生在那个街区里面。

有什么办法可以完成我需要的吗?

4

3 回答 3

1

编辑:这种方法可能不适用于处理异常

经过反思,我在下面描述的解决方案可能不适用于捕获和处理异常,至少在没有额外努力的情况下不会。问题是,如果您从语句范围内抛出异常using,并且您没有在using语句范围内捕获异常(例如,它在调用堆栈中被进一步捕获),则该using语句将超出范围在异常被捕获之前。因此,在 using 语句中抛出的异常由异常管理器处理之前,将调用Disposein 方法并从堆栈中弹出上下文。ExceptionManagerContext


这是您捕获和记录或以其他方式处理异常的类。您可以向其中添加一个Stack对象IExceptionManagerContext,以便您可以PushPop上下文。

class ExceptionManager
{
    private Stack<IExceptionManagerContext> contexts;

    public ExceptionManager()
    {
        contexts = new Stack<IExceptionManagerContext>();
        PushContext(new DefaultExceptionManagerContext());
    }

    public void PushContext(IExceptionManagerContext context)
    {
        contexts.Push(context);
    }

    public void PopContext()
    {
        contexts.Pop();
    }

    private IExceptionManagerContext CurrentContext
    {
        get { return contexts.Peek(); }
    }

    public void Handle(Exception ex)
    {
        if (CurrentContext.EnableLogging) 
        {
            Log(ex);
        }
        else
        {
            DoSomethingElseWith(ex);
        }
    }
}

堆栈底部的默认上下文可以实现一些默认选项,例如启用日志记录。您可以添加任意数量的其他属性以IExceptionManagerContext使您的异常管理可配置。

interface IExceptionManagerContext
{
    bool EnableLogging { get; }
}

class DefaultExceptionManagerContext : IExceptionManagerContext
{
    public bool EnableLogging { get { return true; } }
}

实现了一个自定义上下文类IDisposable,以便它可以安全地将自己推入/弹出异常管理器中的上下文堆栈。此类将需要对异常管理器的某种引用,或者您的管理器可能是单例等。请注意,这是 IDisposable 的简单实现,它的某些用法可能会导致PopContext被调用的次数多于PushContext,因此您可能想要改进实施以确保安全。

class ExceptionManagerContext : IExceptionManagerContext, IDisposable
{
    ExceptionManager manager;

    public ExceptionManagerContext(ExceptionManager manager)
    {
        this.manager = manager;
        manager.PushContext(this);
    }

    public bool EnableLogging { get; set; }

    public void Dispose()
    {
        manager.PopContext();
    }
}

这是您想要定义一个“上下文”的代码,其中异常处理的行为应该不同。该using语句将确保设置更改的上下文,然后安全地删除。您可以嵌套这样的 using 语句,递归地运行此代码等,它应该做正确的事情。

using (new ExceptionManagerContext(exceptionManager) { EnableLogging = false })
{
    DoSomethingThatMayRaiseExceptions();
}
于 2013-04-26T15:49:50.683 回答
1

这是一种不同的方法。创建一个Exception标记不应记录的异常的子类(否则应由您的异常管理器以不同方式处理):

class UnloggedException : Exception
{
    public UnloggedException(Exception innerException)
        : base(innerException.Message, innerException)
    {   
    }
}

然后,而不是using像我发布的另一个答案中的声明,使用一个 try/catch 块,您想在其中为不同的异常处理定义一个“上下文”:

try
{
    DoSomethingThatMayRaiseExceptions();
}
catch (Exception ex)
{
    throw new UnloggedException(ex);
}

这将包装在新异常类中抛出的原始异常。然后,您的异常管理器可以检查异常是否为 type UnloggedException,并以不同的方式处理它。InnerException它仍然可以通过类的属性访问抛出的原始异常及其堆栈跟踪等UnloggedException

于 2013-04-26T16:31:54.547 回答
0

您可以使用框架中内置的现有代码访问安全性内容,为您进行堆栈遍历和属性检查。只需定义一个自定义权限,并在发生错误的调用树深处检查该权限。

请注意,如果异常处理程序位于调用者而不是被调用者中,则此方法以及代码和 esker 答案中建议的自定义堆栈管理都将不起作用,因为堆栈帧和using块已经展开。第一次机会异常处理可以解决这个问题,但在 C# 代码中不可用。.NET 确实支持它,通过 C++/CLI 或 VB.NET(可能还有一些第三方语言)。

于 2013-04-26T16:01:53.230 回答