【问题标题】:What is the reason that out parameters cannot be optional? [duplicate]out参数不能是可选的原因是什么? [复制]
【发布时间】:2015-07-16 05:36:08
【问题描述】:

我不明白为什么在调用带有out 参数的方法时必须声明一个变量,即使我不关心该方法提供的out 值。

在我看来,这类似于调用具有返回值bool Foo() 但不使用它Foo(); 的方法。可以将out 参数标记为可选参数将使我的代码更简洁,或者让API 开发人员无需为该方法编写一个没有out 参数的重载。

那么,out 参数不能是可选的原因是什么?

【问题讨论】:

  • 你有一个例子说明这是可选的吗?我能想到的所有时间,out 参数都可以提供有用的信息
  • @Sayse 您可以使用int.TryParse 来检查字符串是否为数字,而对其值不感兴趣。有一些忽略输出参数的用例。
  • @SonerGönül 没有解释原因。
  • @xanatos - 是的,但与此同时,如果它是一个数字,那么您很可能希望稍后在某个地方使用该数字
  • 请记住,out 自 C# 1.0 以来就已存在。 Optional 是在很久以后才引入的(对 C#)。

标签: c#


【解决方案1】:

我认为没有真正的理由不能这样做。也许原因只是:因为它已记录在案。

据我所知,没有任何编译器魔法使out 与众不同。看看这个 CIL 代码:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       17 (0x11)
  .maxstack  1
  .locals init ([0] int32 y)
  IL_0000:  nop
  IL_0001:  ldloca.s   y
  IL_0003:  call       void ConsoleApplication15.Program::X(int32&)
  IL_0008:  nop
  IL_0009:  ldloc.0
  IL_000a:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000f:  nop
  IL_0010:  ret
} // end of method Program::Main

.method public hidebysig static void  X([out] int32& i) cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldc.i4.s   10
  IL_0004:  stind.i4
  IL_0005:  ret
} // end of method Program::X

源自此方法:

public static void X(out int i)
{
    i = 10;
}

static void Main(string[] args)
{
    int y;
    X(out y);

    Console.WriteLine(y);
}

如您所见,变量是在调用方法中分配的,并且值是“通过引用”传递的。为out 参数“just”定义默认值会中断检查是否设置了值(因为它已经设置了)。

另外,作为Damien_The_Unbelievercommented,它可能只是在可选参数的初始构建中被忽略/跳过的一个功能,因为当时out已经存在。

【讨论】:

  • 使用这个推理,即使是可选参数也是编译器的一个技巧,它们在 CIL 级别不存在。 out 和“可选”都是编译器技巧,元属性轻微支持
  • @xanatos:有什么参考吗? [opt] 也是 CIL 参数属性。
  • 反汇编goo.gl/ulNaS4。 5 设置调用方(IL_0001:ldc.i4.5)
  • 是的......也许我误读了你的答案......现在我重读了它,没有什么我不同意的(或者更清楚地说:我同意你的所有回答) .
  • 编译器很难为变量提供默认分配(对于引用类型,唯一合乎逻辑的事情是 null) 但这是错误的。 .. default(T) 在 C# 中始终有效
【解决方案2】:

Out 参数是编译器技巧。在 CLR 世界中没有任何称为 out 的参数。传递给方法的实际上是ref 参数。

不同之处在于编译器将确保在方法退出之前分配值。就是这样。

因此,当您需要使用 ref/out 参数调用方法时,您不需要值而是对变量/字段的引用。

对于可选参数,c#编译器在调用方法时会传递默认值,但是这里不能;你需要一个参考。

如果编译器必须支持这个特性,它必须为你创建一个变量,通过引用传递它并忽略结果。如您所见,这是一件很丑陋的事情,因此我们没有该功能。

【讨论】:

  • 在这个讨论中,我认为对否决票的解释会非常有用,所以请投反对票。
  • @PatrickHofman Stackoverflow 我猜已经变成了元数据。如果人们不喜欢您的回答,他们往往会投反对票:\
  • 嗯,我发现最后一句话很有趣(如果它是一个对象并且没有默认构造函数怎么办......)。我想你可能是对的。 +1
  • @Patrick Homan:是和否。 using 内部的值分配给隐藏的本地。隐藏的本地在失去范围时被释放,而不是给定的本地。您可以尝试这个来更改使用范围内的给定本地,看看它是否会被释放。您将看到原始对象将被处置,而不是新分配的值。
  • 所以...我们创建隐藏的本地人...没关系。我们已经忽略了返回值……这也没关系。那么,当我们将这两者结合起来时,为什么它会变得“丑陋”呢?
【解决方案3】:

“当前”的 C# 编译器(Roslyn 之前的版本)相当复杂,在 Microsoft,他们仅在绝对必要时才尝试对其进行修改。 COM 互操作所需的可选参数,因此他们添加了它们。也许不需要可选的输出参数。

我们可以希望,有了 Roslyn,编译器的进步会更快,我们将进入一个语法膨胀的新时代:-)

有人在 Roslyn github 上为此提出了功能请求:https://github.com/dotnet/roslyn/issues/186

【讨论】:

  • 在这个讨论中,我认为对否决票的解释会非常有用,所以请投反对票。
  • 我认为编译器的复杂性不是一个论据。编译器解决的问题远不止于此。
  • @MartinMulder 不,很多时候“知道”的人都说没有添加到编译器中的功能,因为 A)除非你真的需要它,否则你不会添加它,B)C# 编译器相当复杂,所以他们不想接触太多
  • 如果(参数 A)他们真的想要,参数 B 几乎总是被拒绝。技术论点几乎总是非论点。有可能,就这么简单。唯一的问题是:他们真的想要吗?
  • @MartinMulder 让我们将它与另一个功能进行比较:C# 6.0 的Null-conditional Operator?. 运算符)...更改编译器可能非常“简单”(如果可以“简单”,您仍然需要更改语法),它完全是必须插入的样板代码(如?? 运算符)。这是一个非常有用的功能(你在代码中写了数百个if (xxx != null) { xxx.DoSomething()))......我们将不得不再等一年才能拥有它。
【解决方案4】:

我认为这个讨论有两个层次。技术层面和功能层面。

在技术层面上,问题应该是:有可能/可以做到吗?在这种情况下,答案是肯定的。对于编译器来说,很容易创建一个隐藏的本地并使用可选的 out 参数调用该方法并引用该隐藏的本地。

在功能层面上,我们应该问自己:这是我们真正想要的吗?

我的看法是:为什么不呢?当我们调用一个函数时,我们已经忽略了返回值以及它的结果。那么为什么不允许我们(自动)忽略来自(可选)输出参数的值呢?

此外,我们可能会与该方法的其他重载发生冲突,但作为状态,我们在使用可选项时已经存在这些冲突,因此没有什么新内容。

归根结底,这取决于 C# 的设计者。他们应该回答这两个问题:有可能吗?我们是否想要这样?

【讨论】:

  • 在这个讨论中,我认为对否决票的解释会非常有用,所以请投反对票。
  • 有可能吗?我们想要这个吗? 不,正确的问题应该是是否有可能,它会让程序员能够做新的有趣的事情( LINQ)删除无用的样板代码(参见自动属性、COM 可选参数、???. 运算符)
  • 如果它增加了一些新的/有趣的东西......那么......我们想要它。我在谈论抽象级别(技术与功能),因此将其带到特定级别。您和我的论点并不相互排斥……一个是另一个的例子。
猜你喜欢
  • 2013-02-16
  • 2021-09-01
  • 2012-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-17
  • 2020-09-09
  • 2023-03-29
  • 2011-10-02
相关资源
最近更新 更多