19

首先让我说我不提倡这种方法,但我最近看到了它,我想知道是否有一个名字可以用来指出有罪的一方。所以这里。

现在你有了一个方法,你想返回一个值。您还想返回错误代码。当然,异常是更好的选择,但无论出于何种原因,您都需要一个错误代码。记住,我在这里扮演魔鬼的拥护者。因此,您创建了一个通用类,如下所示:

class FunctionResult<T>
{
    public T payload;
    public int result;
}

然后像这样声明你的函数:

FunctionResult<string> MyFunction()
{
    FunctionResult<string> result;
    //...

    return result;
}

此模式的一种变体是使用枚举作为错误代码而不是字符串。现在,回到我的问题:有这个名字吗?如果有,它是什么?

4

13 回答 13

17

我同意这不是专门的反模式。根据使用情况,它可能是一种气味。人们实际上不想使用异常是有原因的(例如,对于初学者来说,返回的错误不是“异常的”)。

在某些情况下,您希望服务返回其结果的通用模型,包括错误和良好值。这可能由将结果转换为异常或其他错误结构的低级别服务交互包装,但在服务级别,它允许服务返回结果和状态代码,而无需定义一些可能的异常结构必须跨越远程边界进行翻译。

此代码也不一定是错误:考虑一个 HTTP 响应,它包含许多不同的数据,包括状态代码以及响应的主体。

于 2008-09-16T14:29:08.450 回答
12

它被称为“用异常替换错误代码

于 2008-09-16T14:22:29.247 回答
10

好吧,这不是反模式。C++ 标准库利用了这一特性,.NET 甚至FunctionResult在 .NET 框架中提供了一个特殊的类。它被称为Nullable。是的,这不限于函数结果,但它可以用于这种情况,实际上在这里非常有用。如果 .NET 1.0 已经有了这个Nullable类,那么它肯定会被用于NumberType.TryParse方法,而不是out参数。

于 2008-09-16T14:23:16.623 回答
6

我通常将有效负载作为(而不是 const)引用传递,并将错误代码作为返回值传递。

我是游戏开发者,我们排除异常

于 2008-09-16T14:28:13.133 回答
5

Konrad 是对的,C# 一直使用双重返回值。但我有点喜欢 C# 中的 TryParse、Dictionary.TryGetValue 等方法。

int value;
if (int.TryParse("123", out value)) {
    // use value
}

代替

int? value = int.TryParse("123");
if (value != null) {
    // use value
}

...主要是因为 Nullable 模式不能扩展到非值返回类型(即类实例)。这不适用于 Dictionary.TryGetValue()。并且 TryGetValue 比 KeyNotFoundException 更好(在调试器中没有“第一次机会异常”,可以说更有效),比 Java 的 get() 返回 null 的实践更好(如果预期 null 值怎么办),并且比必须更有效首先调用 ContainsKey()。

但这仍然有点麻烦——因为这看起来像 C#,所以它应该使用一个 out 参数。实例化类可能会丢失所有效率增益。

(可以是 Java,除了“字符串”类型是小写的。在 Java 中,你当然必须使用一个类来模拟双重返回值。)

于 2008-09-16T14:36:59.837 回答
4

我不确定这是一个反模式。出于性能原因,或者可能使该方法可能失败的事实更加明确,我经常看到使用它而不是异常。对我来说,这似乎是个人偏好而不是反模式。

于 2008-09-16T14:19:34.433 回答
3

我同意那些说这不是反模式的人的观点。在某些情况下,它是一种完全有效的模式。例外是针对例外情况,应在预期情况下使用返回值(如您的示例中)。一些领域期望类的有效和无效结果,并且这些都不应该被建模为异常。

例如,给定 X 油量,汽车可以从 A 到 B,如果可以,还剩下多少油?这种问题非常适合您提供的数据结构。预计无法从 A 到 B,因此不应使用例外。

于 2008-09-16T15:39:02.243 回答
2

这种方法实际上比我见过的其他方法要好得多。例如,C 中的某些函数在遇到错误时会返回并且似乎成功了。判断它们失败的唯一方法是调用将获得最新错误的函数。

在我终于发现 sem_init 在 OSX 上不起作用之前,我花了几个小时试图在我的 MacBook 上调试信号量代码!它编译没有错误,运行也没有导致任何错误——但信号量不起作用,我不知道为什么。我很同情那些将使用POSIX 信号量的应用程序移植到 OSX 并且必须处理已经调试过的资源争用问题的人。

于 2008-09-16T14:26:18.817 回答
1

“无法确定这是否是错误”模式怎么样。似乎如果您确实有异常但想返回部分结果,则可以将结果包装在异常中。

于 2008-09-16T14:13:51.747 回答
1

如果您不想使用异常,最简洁的方法是让函数返回错误/成功代码,并采用引用或指针参数来填充结果。

我不会称其为反模式。这是一种经过充分验证的可行方法,通常比使用异常更可取。

于 2008-09-16T14:34:09.187 回答
1

如果您希望您的方法偶尔会失败,但不认为这是异常情况,我更喜欢 .NET Framework 中使用的这种模式:

bool TryMyFunction(out FunctionResult result){    

     //...    
     result = new FunctionResult();
}
于 2008-09-16T14:36:46.467 回答
1

关于气味和反模式的辩论让我想起了“幸存者”电视节目,在那里你有各种编程结构试图在岛上互相投票。我更愿意看到“构造 X 有这样那样的优点和缺点”,而不是不断发展的关于应该做什么和不应该做什么的法令列表。

于 2008-11-19T00:31:49.647 回答
0

为了捍卫反模式指定,此代码适用于以下几种方式:

  1. 对象 x = MyFunction().payload; (忽略返回结果 - 非常糟糕)
  2. int 代码 = MyFunction().result; (扔掉有效载荷 - 如果这是预期用途,可能没问题。)
  3. 函数结果 x = MyFunction(); //... (一堆额外的 FunctionResult 对象和额外的代码来检查它们)

如果您需要使用返回码,那很好。但是然后使用返回码。不要尝试在其中打包额外的有效负载。这就是refout参数 (C#) 的用途。可空类型可能是一个例外,但这只是因为语言中有额外的糖来支持它。

如果您仍然不同意此评估,请否决此答案(不是整个问题)。如果您确实认为这是一种反模式,那么请支持它。我们将使用此答案来了解社区的想法。

于 2008-09-16T17:03:56.690 回答