为什么要有一个返回bool / int并将实际对象作为输出参数的方法?


12

我在公司代码库(.NET 3.5应用程序)的各处看到以下代码模式:

bool Foo(int barID, out Baz bazObject) { 
    try { 
            // do stuff
            bazObject = someResponseObject;

            return true;
    }
    catch (Exception ex) { 
        // log error
        return false;
    }
}

// calling code
BazObject baz = new BazObject();
fooObject.Foo(barID, out baz);

if (baz != null) { 
    // do stuff with baz
}

我试图绕开为什么要这样做,而不是让Foo方法简单地获取ID并返回一个Baz对象,而不是返回未使用的值并使实际对象成为ref或output参数,这是我的头绪。

我缺少这种编码样式的一些隐藏优势吗?



在你的榜样,baznull与返回的bool存在false等价的。new BazObject()从来都不是null,所以除非bazObject之前被更新Exception时抛出Foo,当false返回baz永远不会null。如果有相应的规范,那将有很大的帮助Foo。实际上,这也许是此代码所表现出的最严重的问题。
史蒂夫·鲍威尔

我的记忆很模糊,因为这是很久以前的事了,但是我认为我弄乱了这个示例,它正在检查“ baz”是否为假,而不是为null。无论如何,这种模式对我来说似乎都是过时的,就像来自VB6一样,并且开发人员从来没有费心去改进他的代码(他没有这样做)
Wayne Molina 2014年

Answers:


11

您通常使用该模式,因此可以编写如下代码:

if (Foo(barId, out bazObject))
{
  //DoStuff with bazobject
}

例如,它在字典类的CLR中用于TryGetValue。它避免了一些冗余,但是out和ref参数在我看来总是有些混乱


1
+1,这就是为什么尽管我倾向于同时返回布尔值和对象而不是分别返回它们的原因
pdr,

因为它确实解释了原因,所以将其标记为答案,尽管共识似乎是,除了在特定情况下,它已经过时了。
韦恩·莫利纳

这个答案证明了使用这种模式是合理的。但是,如果它遍及您的代码库,则可能像Sean side那样是一种不好的做法。
Codism 2011年

11

在出现异常之前,这是旧的C样式代码。返回值指示该方法是否成功,如果成功,则将用结果填充参数。

在.net中,我们为此有例外。应该没有理由遵循这种模式。

[edit]异常处理显然对性能有影响。也许与它有关。但是,在该代码段中已经抛出了异常。只是让它在堆栈中向上移动直到被卡在更合适的位置,这样会更清洁。


不。不同意。切勿在预期情况下使用异常。例如,如果要将字符串解析为int,则总是有可能有人将传递无法解析的字符串。在这种情况下,您不应抛出异常
-Pdr

我不是那个意思 如果您阅读了OP发布的代码,则显然会发生异常情况,但是该方法将捕获它并返回false。这个问题与错误处理有关,与预期条件无关。
肖恩·爱德华兹

是的,它似乎主要用于从数据库中加载数据的方法中,例如,if (baz.Select())但往往不是返回值就被丢弃,而是针对null或某些属性检查该值。
韦恩·莫利纳

@Sean,看看您在说什么,我只是反对短语“在.NET中,我们为此目的有例外。” 例外服务完全不同的目的,对我来说
PDR

1
好吧,让我说清楚。的确,您不应该在每个方法中都添加一个布尔值来解决无效参数上的编码错误。但是,如果方法的输入来自用户而不是开发人员,则您应该能够尝试处理输入而不会在输入错误的情况下引发异常。
pdr

2

给定代码段,它看起来完全没有意义。对我而言,初始代码模式将建议BazObject的null为可接受的情况,并且布尔返回值是安全确定失败情况的一种措施。如果以下代码是:

// calling code
BazObject baz = new BazObject();
bool result = fooObject.Foo(barID, out baz);

if (result) { 
    // do stuff with baz
    // where baz may be 
    // null without a 
    // thrown exception
}

这样对我来说更有意义。也许这是您之前用来确保通过引用传递baz而不了解对象参数在C#中实际工作方式的一种方法。


我认为它是由现任团队成员撰写的。也许我应该为O_O担心-Wayne
Molina

@Wayne M:担心而不是潜在的培训机会:)
Joel Etherton

1

有时,当您(调用方)不关心返回的类型是引用类型还是值类型时,此模式很有用。如果要调用这样的方法来检索值类型,则该值类型将需要一个已建立的无效值(例如,double.NaN),或者需要其他确定成功的方法。


那讲得通。似乎有些怪异,但这听起来像是有道理的。
韦恩·莫利纳

0

这个想法是返回一个值,该值指示该过程是否成功,并允许如下代码:

Baz b;
if (fooObject.foo(id, out b)) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

如果对象不能为null(在您的示例中显示为正确),则最好执行以下操作:

Baz b = fooObject.foo(id);
if (b != null) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

如果对象可以为null,则可以使用异常处理:

try {
   Baz b = fooObject.foo(id);
}
catch (BazException e) {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

这是C语言中常用的模式,没有例外。它允许函数返回错误条件。通常,例外是更干净的解决方案。


特别是因为代码已经引发异常。
David Thornley,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.