我何时应该使用参数?

时间:2009-07-23 05:36:47

标签: c# .net out

我不明白何时应该使用输出参数,如果我需要返回多个类型,我个人将结果包装成新类型,我发现使用它比使用更容易。

我见过像这样的方法,

   public void Do(int arg1, int arg2, out int result)

是否存在实际有意义的情况?

TryParse怎么样,为什么不返回ParseResult类型?或者在较新的框架中返回一个null-able类型?

10 个答案:

答案 0 :(得分:26)

当你有一个TryNNN函数时,out很好,并且很明显即使函数没有成功,也会设置out参数。这允许您依赖于您声明的局部变量将被设置的事实,而不是必须在稍后的代码中对null进行检查。 (下面的注释表明该参数可以设置为null,因此您可能需要验证您正在调用的函数的文档,以确定是否是这种情况。)它使代码成为更清晰,更容易阅读。另一种情况是,您需要返回一些数据和状态,如下所示:

public bool DoSomething(int arg1, out string result);

在这种情况下,返回可以指示函数是否成功,结果是否存储在out参数中。不可否认,这个例子是设计的,因为你可以设计一种方法,函数只返回string,但你明白了。

缺点是您必须声明一个局部变量才能使用它们:

string result;
if (DoSomething(5, out result))
    UpdateWithResult(result);

而不是:

UpdateWithResult(DoSomething(5));

然而,这可能不是一个缺点,它取决于你想要的设计。在DateTime的情况下,提供了两种方法(Parse和TryParse)。

答案 1 :(得分:5)

与大多数事情有关。 让我们看一下选项

  • 你可以返回任何你想要的函数作为函数的返回值
  • 如果要返回多个值或函数已经有返回值,则可以使用params或创建一个新的复合类型,将所有这些值公开为属性

在TryParse的情况下,使用out param是有效的 - 你不必创建一个新的类型,它将是16B的开销(在32b机器上)或者在调用后产生垃圾收集的性能。例如,可以在循环内调用TryParse - 所以在这里指出params规则 对于不在循环中调用的函数(即性能不是主要问题),返回单个复合对象可能是“更清晰”(对于旁观者来说是主观的)。现在使用匿名类型和动态类型,它可能变得更加容易。

注意:

  1. out params有一些需要遵循的规则,即编译器将确保函数在退出之前初始化值。因此,即使解析操作失败,TryParse也必须将out参数设置为某个值
  2. TryXXX模式是何时使用params的一个很好的例子 - Int32.TryParse被引入了coz人们抱怨捕获异常以确定解析是否失败。在解析成功的情况下,你最有可能做的就是获得解析后的值 - 使用out参数意味着你不必再对Parse进行另一个方法调用

答案 2 :(得分:3)

我认为out对于需要返回布尔值和值的情况很有用,比如TryParse,但是如果编译器允许这样的话会很好:

bool isValid = int.TryParse("100", out int result = 0);

答案 3 :(得分:2)

当然,如果您有一个需要返回多个值的方法,那么在您发布的示例中,将使用out参数:

public void Do(int arg1, int arg2, out int result)

使用out参数没有多大意义,因为您只返回一个值,如果删除out参数并放入int返回值,则可以更好地使用该方法:

public int Do(int arg1, int arg2)

关于参数的一些好处:

  1. 输出参数最初被视为未分配。
    • 在方法返回之前,必须明确指定每个out参数 ,如果您错过了作业,则代码将无法编译。
  2. 总之,我基本上尝试在我的私有API 中使用params来避免创建单独的类型来包装多个返回值,而在我的公共API上,我只在与它匹配的方法上使用它们TryParse模式。

答案 4 :(得分:2)

我知道,答案已经很晚了。 如果您不希望您的方法实例化要返回的新对象,那么out(和ref也是如此)也非常有用。这在您希望为您的方法实现亚微秒性能的高性能系统中非常相关。从内存访问的角度来看,实例化是相对昂贵的。

答案 5 :(得分:0)

为返回值创建一个类型对我来说听起来很痛苦:-) 首先,我必须创建一个返回值的类型然后在调用方法中我已经将返回类型的值赋给需要它的实际变量。

输出参数比较简单。

答案 6 :(得分:0)

是的,它确实有意义。以此为例。

String strNum = "-1";
Int32 outNum;

if (Int32.TryParse(strNum, out outNum)) {
    // success
}
else {
    // fail
}

如果操作在具有返回值的正常函数中失败,您会返回什么?你当然不能返回-1来表示失败,因为那时失败返回值和正在解析的实际值之间没有区别。这就是为什么我们返回一个布尔值来查看它是否成功,如果它成功了,那么我们已经安全地分配了我们的“返回”值。

答案 7 :(得分:0)

令我烦恼的是,我无法将null传递给TryParse函数的out参数。

但是,在某些情况下,我更喜欢使用两个数据返回一个新类型。特别是当他们大部分时间不相关或者一件事后,只需要一次操作。当我确实需要保存TryParse函数的结果值时,我真的很喜欢有一个out参数而不是我必须处理的一些随机的ResultAndValue类。

答案 8 :(得分:0)

我有时会使用 out 参数以提高可读性,因为读取方法名称比方法输出更重要 - 特别是对于执行命令以及返回结果的方法。

StatusInfo a, b, c;

Initialize(out a);
Validate(a, out b);
Process(b, out c);

VS

StatusInfo a = Initialize();
StatusInfo b = Validate(a);
StatusInfo c = Process(b);

至少对我而言,我在扫描时非常强调每行的前几个字符。在确认声明了一些“StatusInfo”变量之后,我可以很容易地告诉第一个例子中发生了什么。在第二个例子中,我看到的第一件事就是检索了一堆StatusInfo。我必须再次扫描,看看方法可能会产生什么样的影响。

答案 9 :(得分:0)

如果您总是创建一个类型,那么您的应用程序中可能会出现大量混乱。

如上所述,一个典型的用例是TrySomething方法,您希望返回bool作为成功的指标,然后返回实际值。我还发现在if语句中有点清洁 - 所有三个选项大致都具有相同的LOC。

int myoutvalue;
if(int.TryParse("213",out myoutvalue){
    DoSomethingWith(myoutvalue);
}

vs.

ParseResult<int> myoutvalue = int.TryParse("213");
if ( myoutvalue.Success ) {
    DoSomethingWith(myoutvalue.Value);
}

vs.

int? myoutvalue = int.TryParse("213");
if(myoutvalue.HasValue){
    DoSomethingWith(myoutvalue.Value);
}

至于“为什么不返回可空类型”:TryParse自Framework 1.x起存在,而Nullable Types附带2.0(因为它们需要Generics)。那么为什么不必要地破坏兼容性或开始在某些类型上引入TryParse之间的不一致?您始终可以编写自己的扩展方法来复制已存在的功能(请参阅Eric Lipperts Post一个不相关的主题,其中包括执行/不执行操作的一些原因)

另一个用例是,如果你必须返回多个不相关的值,即使你这样做会触发警报,你的方法可能做得太多了。另一方面,如果您的Method类似于昂贵的数据库或Web服务调用,并且您想要缓存结果,那么这样做可能是有意义的。当然,您可以创建一个类型,但同样,这意味着您的应用程序中还有一种类型。