使用通用类通过 try/catch/finally 执行?

发布于 2024-09-05 09:43:35 字数 1429 浏览 1 评论 0原文

我发现自己在代码中以不同的方法有很多这样的内容:

try
{
  runABunchOfMethods();
}
catch (Exception ex)
{
  logger.Log(ex);
}

创建这个怎么样:

public static class Executor
{

    private static ILogger logger;

    public delegate void ExecuteThis();

    static Executor()
    {
        // logger = ...GetLoggerFromIoC();
    }

    public static void Execute<T>(ExecuteThis executeThis)
        where T : Exception
    {
        try
        {
            executeThis();
        }
        catch (T ex)
        {
            // Some kind of Exception Handling Strategy...
            logger.Log(ex);
            // throw;
        }
    }

}

然后像这样使用它:

private void RunSomething()
{
  Method1(someClassVar);
  Method2(someOtherClassVar);
}

...

Executor.Execute<ApplicationException>(RunSomething);

这种方法有什么缺点吗? (当你想要一个finally时,你可以添加执行器方法和委托,并使用泛型来捕获你想要捕获的异常类型...)

编辑:抱歉不清楚 - 我真正想要的是关于尝试将代码的执行从相关类转移到执行此操作的更通用类的一般想法的一些输入。我只是做了一个解决方案的快速模型,但在现实生活中,您自然会使用诸如异常处理策略、抽象执行基类以及针对系统的特定层/部分的更专门的执行类之类的东西。我通常使用 try.../runABunchOfMethods 部分创建一个方法(这会执行异常处理,并带有专门的异常),该方法调用 runABunchOfMethods,而 runABunchOfMethods 又会执行一组有限的其他方法“干净代码”风格。

我会在某种程度上购买混淆论点,但如果整个解决方案/架构采用这种建议的方法,那么新程序员应该能够理解该模式。

我编辑了执行器以包含通用 T,以允许调用代码指定异常,只是为了展示如何处理专门的异常。在其他情况下,您可能会遇到很多问题:取决于您想要做什么,但这些是我正在讨论的具体子类中的特殊情况。

I find myself having a lot of this in different methods in my code:

try
{
  runABunchOfMethods();
}
catch (Exception ex)
{
  logger.Log(ex);
}

What about creating this:

public static class Executor
{

    private static ILogger logger;

    public delegate void ExecuteThis();

    static Executor()
    {
        // logger = ...GetLoggerFromIoC();
    }

    public static void Execute<T>(ExecuteThis executeThis)
        where T : Exception
    {
        try
        {
            executeThis();
        }
        catch (T ex)
        {
            // Some kind of Exception Handling Strategy...
            logger.Log(ex);
            // throw;
        }
    }

}

And just using it like this:

private void RunSomething()
{
  Method1(someClassVar);
  Method2(someOtherClassVar);
}

...

Executor.Execute<ApplicationException>(RunSomething);

Are there any downsides to this approach? (You could add Executor-methods and delegates when you want a finally and use generics for the type of Exeception you want to catch...)

Edit: Sorry for being unclear - what I was really after was some input on the general idea of trying to move the execution of code from the class in question to a more generalized class that does this. I just did a quick mock-up of a solution but in real life you would naturally use things such as exception handling strategies, abstract execution base classes with more specialized execution classes for a specific layer/part of the system. I generally create one method with the try.../runABunchOfMethods-part (this does the exception handling, with specialized exceptions) that calls the runABunchOfMethods that in turn execute a limited set of other methods "clean code" style.

I'll buy the obfuscation argument on some levels but if the whole solution/architecture takes this proposed approach a new programmer should be able to understand the pattern.

I've edited the Executor to include a generic T to allow the calling code to specify the exeception just to show how to handle a specialized exeception. In other cases you might have a bunch of catch:es depending on the what you want to do but those are special cases in the concrete subclasses I was talking about.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

扛刀软妹 2024-09-12 09:43:36

下站是一般异常处理的方法。如果没有充分的理由,您不应该捕获基类Exception。它可能隐藏各种问题;好的,您记录了它们,但您的代码不知道它刚刚内存不足,或者文件不存在并继续,就好像没有问题一样全部。
您在此处描述的方法将鼓励进行一般异常处理。

相关:
捕获一般异常真的那么糟糕吗?< /a>
这是吗捕获非特定异常(例如 System.Exception)是一种不好的做法吗?为什么?

编辑(回应OP的编辑和澄清):
我不确定你想实现什么目标。基本上你隐藏了一个异常(一般或指定的 - 它只是被吞掉)。调用您的方法 Executor.Hide( RunSomething ); ,很清楚这里发生了什么。但是吞掉异常有什么好处吗?我不这么认为。同样,有些地方可能需要它 - 但它们很少见,应该有意选择。您提供的方法鼓励不假思索地接受异常。
您注释掉了重新抛出行(throw ex 或更好的是 throw,它保留了堆栈)。启用此行后您会得到什么?基本上只是记录。你捕获一个异常,记录它并重新抛出它......再次捕获它?为什么不能将日志记录到后一个地方?
仅当您能够处理异常时才尝试捕获异常。您也可以在那里登录。任何好的记录器都能够向您显示堆栈跟踪,这样您就不会丢失信息。

相关:
在哪里放置 try catch?

The downsite is your approach of general exception handling. You should not catch the baseclass Exception without a strong reason. It could hide various problems; ok, you log them, but your code doesn't know that it just ran out of memory, or the file does not exist and continue as if there was no problem at all.
The method you describe here would encourage a general exception handling.

Related:
Is it really that bad to catch a general exception?
Is this a bad practice to catch a non-specific exception such as System.Exception? Why?

EDIT (response to the OP's edit & clarification):
I am not sure what you want to achieve. Basicall you hide an exception (general or specified - it is just swallowed). Call your method Executor.Hide<ApplicationException>( RunSomething ); and it is clear what happens here. But is there any advantage in swallowing exceptions? I don't think so. Again, there can be places where you need this - but they are rare and should be chosen intentionally. The method you provide encourages to swallow exception without thinking about it.
You commented out the rethrow line (throw ex or better just throw, which preserves the stack). What do you get with this line enabled? Basically just logging. You catch an exception, log it and rethrow it to ... catch it again? Why can't you put your logging to this latter place?
Try to catch an exception only, when you are able to handle it. There you can log, too. Any good logger will be able to show you the stack trace, so that you have no loss of information.

Related:
Where to put try catch?

空名 2024-09-12 09:43:36

您获得了另一个间接级别,这可能会使您的代码更难以阅读和理解(尤其是对于第一次查看您的代码的人)。

You get another level of indirection which might make your code harder to read and understand (especially for someone looking at your code for the first time).

七度光 2024-09-12 09:43:36

如果你想保持你的对象干净,你可以考虑使用 AOP 框架,比如 PostSharp 。然后,如果您愿意,您的异常记录(例如)可以在一个地方处理。

编辑:

可以使用 postsharp 删除 try / catch 块 - 这是可以在 PostSharp 中创建的示例常见异常处理程序:

[Serializable]
public class CommonExceptionHandling : OnExceptionAspect
{
    public override void OnException(MethodExecutionEventArgs eventArgs)
    {
        // can do some logging here
        // ...

        // throw the exception (out of postsharp) to where it occurred:
        eventArgs.FlowBehavior = FlowBehavior.RethrowException;                       

        // If you want to ignore the exception, you can do this:
        //eventArgs.FlowBehavior = FlowBehavior.Return;

        base.OnException(eventArgs);
    }
}

如果将此属性应用于类,则该类中的任何方法抛出的任何异常都将被通过上面的代码进行指导。

If you want to keep your objects clean, you could consider using an AOP framework like PostSharp. Then your logging of exceptions (for example) can all be handled in one place if you so desire.

EDIT:

It is possible to remove the try / catch blocks using postsharp - here is an example common exception handler that could be created in PostSharp:

[Serializable]
public class CommonExceptionHandling : OnExceptionAspect
{
    public override void OnException(MethodExecutionEventArgs eventArgs)
    {
        // can do some logging here
        // ...

        // throw the exception (out of postsharp) to where it occurred:
        eventArgs.FlowBehavior = FlowBehavior.RethrowException;                       

        // If you want to ignore the exception, you can do this:
        //eventArgs.FlowBehavior = FlowBehavior.Return;

        base.OnException(eventArgs);
    }
}

If you apply this attribute to a class, any exceptions that any methods in that class throw will then be directed through the above code.

屌丝范 2024-09-12 09:43:36

缺点是缺乏灵活性。如果您想从正在调用的方法之一中的特定异常中恢复,例如,在与 SMTP 服务器的连接失败时,您可能希望将出站邮件的内容存储在数据库中以便稍后重试,该怎么办?这就是为什么您应该尽可能避免捕获一般异常类型的原因。

此外,您会失去代码的一些清晰度(无论如何,在我看来),因为很难看出异常处理发生在哪里。

The downside would be a lack of flexibility. What if you wanted to recover from a specific exception in one of the methods being called - e.g. on a failed connection to an SMTP server you may want to store the contents of an outbound mail in your database for retry later? This the reason why you should avoid catching the general Exception type whenever possible.

Also, you lose some of the clarity of your code (in my opinion, anyway) as it is hard to see where exception handling happens.

栖迟 2024-09-12 09:43:36

如果您在很多类中看到该代码,那么您在我的书中做了一些非常错误的事情:-)至少您应该重新抛出异常(只需“抛出”) ,而不是“throw(ex)”),但是始终捕获基本异常只是为了记录它(或者实际上在任何时间,除了一些边缘情况)的整个想法是不正确的对我来说。

我不知道您正在编写什么样的应用程序,但是您不能挂钩诸如 AppDomain.UnhandledException,每当任何线程中存在未处理的异常(您尚未捕获的异常)时都会触发该异常,并记录这些异常?如果您也想记录可恢复的异常,您可能希望将其与也记录的更具体的 catch 语句结合起来。

If you're seeing that code in a lot of classes then you are doing something very very wrong in my book :-) At the very least you should re-throw the exception (with just "throw", not "throw(ex)"), but the whole idea of catching the base exception all the time just to log it (or indeed at any time, bar a few edge cases) is not the right approach for me.

I have no idea what kind of application you're writing, but can you not hook events such as AppDomain.UnhandledException, which fires whenever there's an unhandled exception (one that you haven't caught) in any thread, and log those? You might want to combine that with more specific catch statements that also log, if you want to log recoverable exceptions too.

Spring初心 2024-09-12 09:43:36

你并没有真正赢得任何东西,但你失去了一些东西:

  • 可读性
  • 简单性(你引入了另一个类,读者必须理解的另一个委托)
  • 你不能简单地越过 Executor.Execute 来到达 Method1(...) 。如果您跨过,您将跳过委托中的整个代码块。
  • ...

You don't really win anything, but you lose some things:

  • Readability
  • Simplicity (you introduce another class, another delegate that a reader must understand)
  • You can't simply step over the Executor.Execute to get to Method1(...). If you step over, you skip the whole code block in your delegate.
  • ...
少跟Wǒ拽 2024-09-12 09:43:36

通常,在应用程序中记录异常的最佳模式是在代码中的顶级方法中捕获异常。

  • 在控制台应用程序中,您应该在 Main 中捕获并记录异常。
  • 在 WinForms 应用程序中,您应该在事件处理程序中捕获并记录异常。
  • 在服务中,您应该捕获并记录异常,然后是工作线程的 ThreadProcs。
  • 等等。

在应用程序中的几乎所有其他点,您应该只捕获特定的异常,然后重新抛出包装原始异常的新异常。如果不需要包装原始异常,则不需要首先捕获原始异常。

像这样构造你的应用程序只会导致你在少数地方捕获、记录和“吞掉”异常。

Normally the best pattern for logging exceptions in your application is to only catch exceptions at the top-level methods in your code.

  • In a console application you should catch and log exceptions in Main.
  • In a WinForms application you should catch and log exceptions in your event handlers.
  • In a service you should catch and log exceptions then the ThreadProcs of your worker threads.
  • Etc.

At almost all other points in your application you should only catch specific exceptions and then rethrow a new exception wrapping the original exception. If you don't need to wrap the original exception you don't need to catch the original exception in the first place.

Sructuring your application like this will lead to only a few places where you catch, log and "swallow" exceptions.

踏月而来 2024-09-12 09:43:36

我将加入“它使您可能通过仅在一般级别上捕获来滥用异常”的潮流。但是,在更特殊的情况下,您可能会受益于某种“异常处理委托包装器”。例如,如果您正在使用与数据库相关的类进行大量工作,而这些类总是抛出相同的异常,那么您可以有一个专用的数据库异常包装器。

但归根结底,异常“混乱”您的代码这一事实对于确保代码安全来说是不利的。

I'll jump on the "it makes you likely to misuse exceptions by only catching at a general level" bandwagon. However you might benefit from some sort of 'exception handling delegate wrapper' in more specialized cases. For instance if you were doing lots of work with DB-related classes that always throw the same exceptions, you could have a dedicated DB-exception-wrapper.

Ultimately though, the fact that exceptions'clutter' your code is the downside to making your code safe.

翻身的咸鱼 2024-09-12 09:43:36

从一开始......

您的代码库中就不应该有像下面这样的代码。

try 
{ 
  runABunchOfMethods(); 
} 
catch (Exception ex) 
{ 
  logger.Log(ex); 
} 

我建议您首先应该看看这样的代码的实际需要。您应该在所有情况下捕获非常具体的异常(与通用捕获所有异常不同)。

之后,如果仍然有多个方法捕获相同的异常,那么您可以考虑在执行器中创建多个处理特定异常类型的方法。

At the very begining....

You should NOT have a code like below scattered around in your code base.

try 
{ 
  runABunchOfMethods(); 
} 
catch (Exception ex) 
{ 
  logger.Log(ex); 
} 

I would suggest you should first look at the actual need of a code like this. You should be catching a very specific exception in all the cases ( unlike a generic catch all).

After that, if you still still multiple methods catching same exception then you can think of creating multiple methods in your executor which handle a specific exception type.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文