在方法内生成和处理用户消息?

时间:2011-08-05 15:51:34

标签: c# vb.net design-patterns .net-3.5

处理可能偶尔无法评估的函数的最佳方法是什么,但即使失败也不需要暂停父例程,但有时可能需要向用户解释?

我的代码中有一个返回数字的函数。编码方面,最简单的方法是使函数可以为空,并在无法求值时返回null。这样,调用例程可以继续,同时还知道函数未能返回数字。

但是,根据具体情况,有时我想向用户显示失败的原因,以便他们知道要修复的内容。显然只返回null是不够的信息。我是否应该在函数内部引发消息,因为它会评估要捕获的匿名侦听器,以便在需要时显示? (对于那些正确地指出逻辑函数不应该负责创建用户消息的人,我并不是要暗示函数生成全文消息,只是以某种方式传递问题的原因,以便UI可以在以后解析进入消息)

我认为另一个选项是在函数内部抛出异常,如果它无法计算,则在需要时捕获并解释为用户消息。但是,如上所述,无法评估通常并不意味着停止例程,现在我每次使用它时都必须在函数调用周围放置一个Try ... Catch块。

8 个答案:

答案 0 :(得分:3)

不应将异常处理用于流控制。只针对非常特殊的情况抛出异常。

...咳。是时候下车了。

严肃地说,我不知道你要解决的问题的性质。第一个问题是,算法中的失败真的是失败,还是无法评估是否由NaN值表示,或者0?如果它确实是一个基本的概念失败,算法是否能够在继续之前检查输入?如果是这样,抛出 ArgumentException 或从中派生的类 - 最好是后者。这意味着任何其他消费代码都可以处理一般情况(例如,在IoC方案中),并且您的代码可以处理特定情况,可以合理地预期它可以知道。如果你这样做,我建议任何包含此功能的程序集还应该提供一些静态验证函数,允许调用者在调用可能引发异常的内容之前验证入站参数是否有效。

答案 1 :(得分:1)

如果您的调用代码需要知道如何处理NULL值,那么返回NULL就可以了。在这个例子中,我将记录这样的时间,其中从函数内返回NULL(不可避免的)值。也许该功能可以通过消息框通知用户。这真的取决于我猜你是如何设置的。

编辑:

重新阅读帖子之后,我似乎错过了代码不在任何UI类中。与评论一样,任何用户显示消息都应留给UI层呈现,而不是来自任何其他代码。因此,您需要将一个排序标记返回到UI,以便它可以显示消息。如果这个实例不是函数断开器,那么我会说一个异常链是不可能的。我想这让你不得不在每个函数调用的返回值中确定它(就像你在返回NULL的第一个实例中那样) - 也许是返回值的自定义DataType,带有“Warning”或其他的标志

答案 2 :(得分:1)

您可以将out参数“string”作为示例传递给函数,因此每当函数失败时,通过返回falsenull将原因打印给用户。就像微软对TryParse所做的那样......但是在这里我们也得到了失败的原因:

public bool TrySomeFunction(out string errorMessage) 
{
    try
    {
        //code that may cause exception

        return true;//operation completed successfully
    }
    catch (Exception exception)
    {
        errorMessage = exception.Message;
    }

    return false; 
}

答案 3 :(得分:1)

除非在特殊情况下,否则我不会抛出异常。抛出例外是非常昂贵的。保存它们是非常糟糕的东西,比如提到的missrequiredfield jd。相反,我将返回一个对象,该对象具有成功标志,可空结果,以及调用例程随后可以向用户显示的消息列表(或者传递回下一层以显示给用户)

答案 4 :(得分:1)

您可以使用Generic Result类包装所需的信息。

public class MyClass
{
    public static void Main()
    {
        var result = new MyClass().TrySomeFunction();

        if (result.Succeeded)
        {
            // use it
            MyReturningResultType resultValue = result.GetResultValue();
        }
        else
        {
            // use message
            string message = result.ResultMessage;
        }

    }

    Result<MyReturningResultType> TrySomeFunction()
    {
        try
        {
            MyReturningResultType value = CalculateIt();
            return new Result<MyReturningResultType>(value) { Succeeded = true };
        }
        catch (Exception exception)
        {
            return new Result<MyReturningResultType>(default(MyReturningResultType))
            {
                Succeeded = false,
                ResultMessage = exception.Message
            };
        }
    }
}

public class Result<T>
{
    public Result(T resultValue) { this.ResultValue = resultValue; }
    public bool Succeeded { get; set; }
    public string ResultMessage { get; set; }
    public T GetResultValue()
    {
        if (ResultValue is T)
        {
            return (T)this.ResultValue;
        }

        return default(T);
    }

    private T ResultValue;
}

答案 5 :(得分:0)

为每种失败方法创建一个例外类型。根据这些异常类型创建用户消息。不要害怕将字段添加到您的异常类型,以便您可以完全重新创建有价值的消息。这是我在异常编程中经常看到的最大失败。

例如,如果您有一个用户未填写的必填字段,请抛出一个MissingRequiredFieldException属性MissingFieldName(s)。这样,在堆栈中,您可以向用户输出有关缺少哪些字段的有意义的消息。

关于这种方法的最好的部分是,它不依赖于计算函数来生成用户可读的字符串。这将在适当的地方更高。当你必须国际化时会发生什么?您是否要重构您的功能以根据用户在语言之间切换?听起来像混合问题的主要案例......什么是处理用户输出代码的计算函数?

答案 6 :(得分:0)

对我来说,这听起来真的是关于异常处理。

你可以定义自己的异常类型,当一个条件满足(或不满足)时可以抛出一种异常,而另一种情况则抛出另一种异常。

调用者将根据抛出的异常类型决定做什么,比如通知用户或将失败方法的结果设置为null并继续等等......

答案 7 :(得分:0)

我同意@Tom你不应该抛出异常来控制程序流,因为它们很昂贵。您可能考虑的另一种方法是传递一个委托(这不是我的头脑):

public static void Main()
{
    Action<string> errorTarget = delegate(string s) { Console.WriteLine(s); }; 

    SomeFunction(errorTarget);
}

private static void SomeFunction(Action<string> errorTarget)
{
    ...
    // Send non-fatal errors to the errorTarget
    if (result == null)
    {
        // Build the error message, then call errorTarget
        errorTarget(errorMessage);
    }
    ...
}