返回值和输出参数哪个更好?

如果我们想从一个方法中得到一个值,我们可以使用任意一个返回值,像这样:

public int GetValue();

或:

public void GetValue(out int x);

我真的不明白他们之间的区别,所以,不知道哪个更好。你能解释给我听吗?

谢谢你。

141373 次浏览

我更喜欢下面的例子,而不是这个简单示例中的任何一个。

public int Value
{
get;
private set;
}

但是,它们都非常相似。通常,只有在需要从方法返回多个值时才会使用“ out”。如果您想在方法内部和外部发送一个值,可以选择“ ref”。我的方法是最好的,如果你只是返回一个值,但是如果你想传递一个参数并得到一个值返回一个可能会选择你的第一个选择。

主要是个人偏好

我更喜欢返回值,如果您有多个返回值,您可以将它们包装在 ResultDTO 中

public class Result{
public Person Person {get;set;}
public int Sum {get;set;}
}

什么更好,取决于你的特殊情况。之所以存在 out,是为了方便从一个方法调用返回多个值:

public int ReturnMultiple(int input, out int output1, out int output2)
{
output1 = input + 1;
output2 = input + 2;


return input;
}

所以从定义上来说,一个并不比另一个好。但通常情况下,您会希望使用一个简单的返回,除非您有上述情况的例子。

编辑: 这个示例演示了关键字存在的原因之一。以上这些都不能被认为是最佳实践。

您几乎总是应该使用返回值。“ out”参数对许多 API、组合性等都会产生一些摩擦。

突然想到的最值得注意的例外是当您想要返回多个值时(。Net Framework 直到4.0才有元组,比如 TryParse模式。

返回值几乎是 一直都是,当方法没有其他要返回的值时,这是正确的选择。(实际上,如果可以选择的话,我想不出任何情况下 永远不会需要带有 out参数的 void 方法。C # 7用于语言支持的解构的 Deconstruct方法作为这条规则的一个非常非常罕见的例外。)

除此之外,它还可以阻止调用者分别声明变量:

int foo;
GetValue(out foo);

int foo = GetValue();

Out 值还可以防止类似下面这样的方法链接:

Console.WriteLine(GetValue().ToString("g"));

(事实上,这也是属性设置器的问题之一,这也是为什么构建器模式使用返回构建器的方法,例如 myStringBuilder.Append(xxx).Append(yyy)。)

此外,输出参数对于反射来说稍微有点难以使用,并且通常也使得测试更加困难。(模拟返回值比模拟输出参数更容易)。基本上没有什么我能想到的,他们使 更容易..。

返回值 FTW。

编辑: 关于正在发生的事情..。

基本上,当您传入“ out”参数的参数时,您需要传入 变量。(数组元素也被归类为变量。)您调用的方法在其堆栈上没有“新”变量作为参数-它使用您的变量作为存储。变量中的任何更改都立即可见。下面的例子说明了这两者的区别:

using System;


class Test
{
static int value;


static void ShowValue(string description)
{
Console.WriteLine(description + value);
}


static void Main()
{
Console.WriteLine("Return value test...");
value = 5;
value = ReturnValue();
ShowValue("Value after ReturnValue(): ");


value = 5;
Console.WriteLine("Out parameter test...");
OutParameter(out value);
ShowValue("Value after OutParameter(): ");
}


static int ReturnValue()
{
ShowValue("ReturnValue (pre): ");
int tmp = 10;
ShowValue("ReturnValue (post): ");
return tmp;
}


static void OutParameter(out int tmp)
{
ShowValue("OutParameter (pre): ");
tmp = 10;
ShowValue("OutParameter (post): ");
}
}

结果:

Return value test...
ReturnValue (pre): 5
ReturnValue (post): 5
Value after ReturnValue(): 10
Out parameter test...
OutParameter (pre): 5
OutParameter (post): 10
Value after OutParameter(): 10

不同之处在于“ post”步骤——即在局部变量或参数被更改之后。在 Return nValue 测试中,这与静态 value变量没有区别。在 OutParameter 测试中,value变量由 tmp = 10;行更改

您通常应该选择返回值而不是输出参数。如果您发现自己编写的代码需要做两件事,那么 Out params 就是一个必要的罪恶。这方面的一个很好的例子是尝试模式(例如 Int32.TryParse)。

让我们考虑一下这两个方法的调用方需要做什么。作为第一个例子,我可以写这个..。

int foo = GetValue();

注意,我可以在一行中声明一个变量并通过方法赋值它。第二个例子是这样的。

int foo;
GetValue(out foo);

现在我必须预先声明我的变量,然后用两行代码写代码。

更新

问这些类型的问题时,一个很好的地方是。NET 框架设计指引。如果你有书的版本,你可以看到安德斯·海尔斯伯格和其他人对这个主题的注释(第184-185页) ,但在线版本在这里..。

Http://msdn.microsoft.com/en-us/library/ms182131(vs.80).aspx

如果您发现自己需要从 API 返回两个内容,那么将它们包装在 struct/class 中比使用 out 参数要好。

它们都有不同的用途,编译器不会对它们进行相同的处理。如果方法需要返回值,则必须使用 return。Out 用于方法需要返回多个值的地方。

如果使用 return,则首先将数据写入方法堆栈,然后写入调用方法的。如果为 out,则直接写入调用方法堆栈。不知道还有没有别的区别。

您只能有一个返回值,而可以有多个 out 参数。

在这种情况下,您只需要考虑参数。

但是,如果需要从方法返回多个参数,那么可能需要查看从 OO 方法返回的内容,并考虑是否最好返回带有这些参数的对象或结构。因此您又回到了返回值。

没有实际的区别。 Out 参数在 C # 中允许方法返回多个值,仅此而已。

然而,这里有一些细微的差别,但没有一个是真正重要的:

使用 out 参数将强制您使用以下两行:

int n;
GetValue(n);

而使用返回值可以让你一行完成:

int n = GetValue();

另一个区别(仅对值类型进行修正,并且仅当 C # 没有内联函数时)是,使用返回值必然会在函数返回时创建一个值的副本,而使用 OUT 参数不一定会这样做。

正如其他人所说: 返回值,而不是输出参数。

我可以向你推荐《框架设计指南》(第2版)吗?第184-185页介绍了避免输出参数的原因。整本书将引导你在各种正确的方向。NET 编码问题。

与框架设计指南相结合的是静态分析工具 FxCop 的使用。你可以在微软的网站上找到这个免费下载。在编译后的代码中运行这个命令,看看它说了什么。如果它抱怨成百上千的事情... 不要惊慌!冷静而仔细地审视每一个案例。不要急着去解决问题。从它告诉你的东西中学习。你将踏上通往大师之路。

我认为,在使用非托管内存时,这种方法可能会很有用,而且您希望明确指出,应该手动处理“返回”的值,而不是期望它自己被处理掉。

使用 out参数的一个原因还没有被提及: 调用方法必须接收它。如果您的方法产生了一个调用方不应该丢弃的值,那么将其设置为 out将迫使调用方特别接受该值:

 Method1();  // Return values can be discard quite easily, even accidentally


int  resultCode;
Method2(out resultCode);  // Out params are a little harder to ignore

当然,调用者仍然可以忽略 out参数中的 价值,但是您已经引起了他们的注意。

这是一种罕见的需求; 更常见的情况是,您应该为真正的问题使用异常,或者为“ FYI”返回带有状态信息的对象,但是在某些情况下,这一点可能非常重要。

Out 在尝试返回在方法中声明的对象时更有用。

例子

public BookList Find(string key)
{
BookList book; //BookList is a model class
_books.TryGetValue(key, out book) //_books is a concurrent dictionary
//TryGetValue gets an item with matching key and returns it into book.
return book;
}

Return value 是方法返回的正常值。

出去参数中,well out 和 ref 是 C # 的两个关键词,它们允许将变量作为 参考文献传递。

裁判出去的最大区别是,裁判应该在 出去之前被初始化,而 出去不应该

此外,返回值与异步设计范例兼容。

如果函数使用 ref 或 out 参数,则不能将其指定为“异步”。

总之,返回值允许方法链接、更清晰的语法(通过消除调用者声明额外变量的必要性) ,并允许异步设计,而不需要在将来进行大量修改。

使用 out 关键字和返回类型的 bool,有时可以减少代码膨胀并增加可读性。(主要是当 out 参数中的额外信息经常被忽略时。)例如:

var result = DoThing();
if (result.Success)
{
result = DoOtherThing()
if (result.Success)
{
result = DoFinalThing()
if (result.Success)
{
success = true;
}
}
}

Vs:

var result;
if (DoThing(out result))
{
if (DoOtherThing(out result))
{
if (DoFinalThing(out result))
{
success = true;
}
}
}

我怀疑我不会在这个问题上得到一个调查,但我是一个 非常经验丰富的程序员,我希望一些思想更开放的读者会注意到。

我认为,确定性和纯粹性的返回值过程(value-return process,VRP)更适合面向对象程序设计语言。

“ VRP”是作为表达式的一部分调用的函数的现代学术名称,它有一个返回值,在表达式求值期间在概念上取代了调用。例如,在 x = 1 + f(y)这样的语句中,函数 f用作 VRP。

“确定性”意味着函数的结果只取决于其参数的值。如果使用相同的参数值再次调用它,肯定会得到相同的结果。

“纯粹”意味着没有副作用: 调用函数不会对 除了计算结果产生任何影响。这可以解释为意味着没有 很重要副作用,在实践中,所以如果 VRP 输出一个调试消息每次调用,例如,这可能会被忽略。

因此,如果在 C # 中,你的函数不是确定的和纯的,我说你应该使它成为一个 void函数(换句话说,不是一个 VRP) ,并且它需要返回的任何值都应该在 out或者 ref参数中返回。

例如,如果您有一个从数据库表中删除某些行的函数,并且您希望它返回它删除的行数,那么您应该这样声明它:

Public void DeleteBasketItems (BasketItemCategory 类别,out int count) ;

如果您有时想要调用这个函数,但是没有得到 count,那么您总是可以声明一个重载。

你可能想知道这种风格更适合面向对象程序设计。总的来说,它符合一种可以(有点不准确)称为“程序编程”的编程风格,而且它是一种更适合程序编程面向对象程序设计的编程风格。

为什么?对象的经典模型是,它们具有属性(又名属性) ,您可以通过读取和更新这些属性来查询和操作对象(主要)。程序编程样式更容易做到这一点,因为可以在获取和设置属性的操作之间执行任意代码。

程序编程的缺点是,由于可以在任意地方执行任意代码,因此可以通过全局变量和副作用获得一些非常迟钝和容易出现 bug 的交互。

因此,非常简单,对于读取代码的人来说,信号是一个很好的实践,函数可能会因为使其无值返回而产生副作用。

请避免使用 out 参数。

尽管在某些情况下(例如在实现 尝试-解析模式时)它们可能有意义,但是它们非常难以理解。

自己和其他开发人员(使用您的 API 或可能继承您的代码)引入 bug 或副作用的机会非常高(除非您对这个概念非常有经验)。

根据微软的质量规则 CA1021:

尽管返回值很常见并且被大量使用,但是 out 和 ref 参数的正确应用需要中级设计和编码技巧。为普通用户设计的图书馆架构师不应该期望用户能够熟练地使用 out 或 ref 参数。

因此,如果没有很好的理由,请不要使用 outref

参见: