【问题标题】:Are there cases where executing a lambda expression behaves differently from executing the code inline?是否存在执行 lambda 表达式的行为与执行内联代码不同的情况?
【发布时间】:2023-03-03 07:31:19
【问题描述】:

为了简化我的 try 块,我创建了一个静态函数来处理错误捕获,因为无论如何我已经在所有方法中以相同的方式捕获错误。

static class Exceptional
{
    public static Exception Try(Action action, [CallerMemberName] string cmn = "")
    {
        try
        {
            action();
            return null;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace, cmn + ": " + ex.GetType().Name);
            return ex;
        }
    }
}

然后我已经替换了所有情况

        try
        {
            ...
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + Environment.NewLine + ex.StackTrace);
        }

        Exceptional.Try(() =>
        {
            ...
        });

这不仅简化了将我的代码包装在 try 块中的过程,而且还为我提供了一个集中的地方来全局更改我处理异常的方式。

我知道捕获所有异常并一视同仁地对待它们并不是一个好习惯。我知道我应该只捕获我期望抛出的异常类型,但现在我只是在测试东西,我不知道会抛出什么,所以我一直在使用try块以确保我不会出现无声或无法解释的故障。这不是我的问题所在。

我想知道这样做是否有改变代码行为的危险。显然调用堆栈正在通过向其中插入一个附加函数而被更改,但是这个额外的函数可能会导致意想不到的问题吗?欢迎批评!谢谢。

编辑:我应该提到我目前正在将整个方法体包装在我的 Exceptional.Try 调用中,所以我特别想知道在这种情况下我是否会遇到奇怪的行为,而不是 lambda 表达式之外的东西可能导致奇怪的行为。

【问题讨论】:

  • “我一直在使用try 块来确保我不会出现静默失败” - 什么???如果您根本不使用异常处理,那么您会看到异常 - 使用 try 块允许静默失败。您应该只捕获可以从中恢复并且可以有意义地处理的异常。你几乎不应该这样做catch (Exception)
  • 好的。我有时会遇到无声的失败。如果您打开异常设置,您可以选择它中断的异常,所以这可能与它有关。但无论如何,无声的失败并不是唯一的问题。我还想控制我从异常中获得的信息。
  • 哦,你知道发生了什么吗?这本身并不是无声的失败,但程序只是崩溃而没有说明原因。
  • 您可以放置​​应用程序级别的异常来捕获所有内容。到处放try 块只是浪费你的时间。阅读以下内容:blogs.msdn.microsoft.com/ericlippert/2008/09/10/…
  • 哦,好的,谢谢。

标签: c# function methods lambda try-catch


【解决方案1】:

这应该可以正常工作。正如您所提到的,额外的函数调用将显示在调用堆栈中,但这应该不会让任何人感到困惑。

一些建议...ToString() 方法返回您正在寻找的所有信息以及任何内部异常。除非您愿意,否则我认为不需要返回异常类型。

public static void Try(Action action, [CallerMemberName] string cmn = "")
{
    try
    {
        action();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }
}

【讨论】:

  • 谢谢。由于我的集中式 catch 语句,这很容易实现。
【解决方案2】:

它会起作用的。它还将改变您的代码的行为方式。这是一个测试方法:

[TestMethod]
public void TestMethod1()
{
    int output = 0;
    Exceptional.Try(() => output = 2 + 2);
    Assert.AreEqual(4, output);
}

但是你会遇到一些问题:

如果抛出异常,代码执行不会自动停止。例如,您可以这样做:

Exceptional.Try(() => DoSomethingThatThrowsAnException());

除非您声明另一个变量来保存返回值(异常或 null),否则您不知道是否有任何问题(假设 MessageBox 只是用于测试。)因此它采用了 C# 的可预测行为并转向它变成了不可预测的东西。有人必须检查该类以查看他们执行方法时发生的情况。因此,您现在正在编写try/catch,但您必须遵循新约定,检查异常的返回值。与默认行为不同的是,如果您忘记检查,则永远不会引发异常。

我必须单独声明output 变量初始化它,即使我稍后要为其分配另一个值。那是因为我不能只做var output = 2 + 2。如果我只是在没有初始化的情况下声明它,那么当我稍后尝试使用该值时,编译器会抱怨该变量可能未初始化。这是因为它不知道该操作是立即执行还是稍后(或永远)执行。

异常和 try/catch 的默认行为是好的,每个人都理解它,所以我不建议做任何改变它。如果发生异常,则执行应该转移到异常处理程序或引发到调用方法。

我建议看看interceptors。它们提供了一种将异常处理与您的代码分开的方法。

【讨论】:

  • 是的 - 我应该澄清一下 - 声明和赋值将发生在同一行,但现在该方法没有返回值。
  • 我的意思是整个事情只是为了测试。因此,由于显示了消息框,因此代码执行当然会停止。
  • 它会停止,但会在消息框之后继续。如果一行代码抛出异常,则不应执行下一行。
  • 哦,我明白了。无论如何,我现在的做法是将整个方法体包装在 try 块中。我知道这不是它应该工作的方式,但这只是我自己的测试套件,而不是共享或用于产品的代码库。
猜你喜欢
  • 1970-01-01
  • 2021-12-02
  • 2011-03-20
  • 1970-01-01
  • 2016-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多