C#可选参数或方法重载?
由于C#添加了可选参数,因此使用可选参数或方法重载被认为是更好的做法,或者在特定情况下,您希望使用其中一个而不是另一个。i、 e一个有很多参数的函数更适合有可选参数的函数?我不确定是否有一个标准答案——这是主观的,而且是逐案的。然而,在我看来,可选参数会创建更显式的API,因此,我通常更喜欢它们而不是方法重载 具体而言,在使用Intellisense时,我更喜欢看到以下内容: 在这方面:C#可选参数或方法重载?,c#,coding-style,overloading,optional-parameters,C#,Coding Style,Overloading,Optional Parameters,由于C#添加了可选参数,因此使用可选参数或方法重载被认为是更好的做法,或者在特定情况下,您希望使用其中一个而不是另一个。i、 e一个有很多参数的函数更适合有可选参数的函数?我不确定是否有一个标准答案——这是主观的,而且是逐案的。然而,在我看来,可选参数会创建更显式的API,因此,我通常更喜欢它们而不是方法重载 具体而言,在使用Intellisense时,我更喜欢看到以下内容: 在这方面: 如果我不指定,我可能不得不猜测(或查找文档)param1和param2的值是多少。可选参数用于促进C
如果我不指定,我可能不得不猜测(或查找文档)param1和param2的值是多少。可选参数用于促进COM对象交互,因为COM对象使用大量可选参数。因此,如果您正在执行P/Invoke或COM对象,请选择可选参数。否则,方法重载是正确的方法,因为它可以避免很多混乱。可选参数需要一个默认值(我仅假设),因此在某些情况下可能很难提供一个默认值。为什么?有些类需要运行时信息才能初始化,而编译器可能无法使用这些信息
当涉及到基本类型时,答案可能与是否可以假定默认值有关,或者如果缺少参数可能意味着方法的不同行为。与重载或命名可选参数不同,我越来越喜欢对象初始值设定项。该类中所需的只是一个无参数构造函数,以及任何要在初始化时设置的公共属性 假设类栏有公共字符串属性“Name”和“Pet”,您可以像这样构造一个新对象
var foo = new Bar { Name = "Fred", Pet = "Dino" };
其优点是,对于要初始化的每个值组合,不需要单独的重载。可选参数很好,但应该在有意义时使用。可选参数通常会混淆方法的意图——如果有其他替代方案,我会倾向于该替代方案 需要可选参数和命名参数的部分原因是COM允许可选参数和名称参数: MSDN 一些API,尤其是COM接口 例如办公自动化API, 是专门用命名的 和可选参数。向上的 到目前为止,这是非常痛苦的 从C#调用这些API,使用 有时多达三十个论点 必须显式传递,大多数 其中有合理的违约 值,可以省略 forums.asp.net中的SomeNewKid简洁地说: …重载方法通常是 优于可选参数。 为什么?保留你的每一种方法 目的明确。即每种方法 我们应该做好一件事。一旦 引入可选参数后 都在稀释它的清洁度 方法,并引入分支 逻辑可能是最好避免的 指一种方法。目的的明确性 当你 开始使用继承。如果你 重写具有一个或多个 可选参数,它们将成为 更难相处。所以,我建议 除了快速和安全之外,什么都可以 脏类,在中使用重载 对可选参数的偏好 请记住,可选参数是一种语法糖: 反射器C:
public class Class1
{
// Methods
public Class1()
{
this.Method1("3", "23");
}
public void Method1(string one, [Optional, DefaultParameterValue("23")] string two)
{
}
}
.class public auto ansi beforefieldinit Class1
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: call instance void [mscorlib]System.Object::.ctor()
L_0006: nop
L_0007: nop
L_0008: ldarg.0
L_0009: ldstr "3"
L_000e: ldstr "23"
L_0013: call instance void WebApplication1.Class1::Method1(string, string)
L_0018: nop
L_0019: nop
L_001a: ret
}
.method public hidebysig instance void Method1(string one, [opt] string two) cil managed
{
.param [2] = string('23')
.maxstack 8
L_0000: nop
L_0001: ret
}
}
IL:
public class Class1
{
// Methods
public Class1()
{
this.Method1("3", "23");
}
public void Method1(string one, [Optional, DefaultParameterValue("23")] string two)
{
}
}
.class public auto ansi beforefieldinit Class1
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
.maxstack 8
L_0000: ldarg.0
L_0001: call instance void [mscorlib]System.Object::.ctor()
L_0006: nop
L_0007: nop
L_0008: ldarg.0
L_0009: ldstr "3"
L_000e: ldstr "23"
L_0013: call instance void WebApplication1.Class1::Method1(string, string)
L_0018: nop
L_0019: nop
L_001a: ret
}
.method public hidebysig instance void Method1(string one, [opt] string two) cil managed
{
.param [2] = string('23')
.maxstack 8
L_0000: nop
L_0001: ret
}
}
VisualStudio和FxCop中的代码分析建议不要在公共API中使用可选参数(规则)。给出的理由是并非所有的.NET语言都支持它们 不过,我认为避免它们的一个更好的理由是,如果在API的2.0版中添加更多重载,它们可能会带来运行时或编译时问题
我将采纳Phil的结论:可选参数旨在支持COM互操作;如果您不使用COM,请不要使用它们。这是一个不错的功能,但它要求您的对象是可变的。我倾向于尽可能地选择不可变类型。不可变和可变都有自己的位置,在C#中,强制不可变可能需要比只读属性和私有设置器更多的工作。任何“更改”都需要构造一个新对象并返回它来代替原始对象。因此,正如在大多数情况下,“这取决于。”@hemp使用可选参数会产生歧义。你最终得到了一种可以做很多事情的方法。该方法将根据其输入具有不同的行为。一种方法应该做一件事,而且只能做一件事。@Chuck我完全不同意,这就是为什么我开始说这个问题本质上是主观的。此外,“一种方法应该做一件事,而且只能做一件事”的论点是一个令人厌倦的论点,经常被滥用。更糟糕的是,我看到的重载方法最常见的实现模式是一个重载调用另一个重载调用,并传入默认值。@Chuck不可能解决“歧义”问题,因为这纯粹是主观的。但是,使用重载方法设置默认值违反了您反对在方法中使用分支逻辑的立场,因为该场景中的最终被调用方根据传入的值的行为不同。虽然我同意“一件事”方法是计算机科学课程中的一件事,提取多个重载方法是有意义的。每个重载都应被视为具有不同参数验证甚至逻辑的用例。正如前面提到的,这在很大程度上是具有启发性的,但我发现为可选参数提供简洁的用例是一个难题,并且可以为一些有趣的bug的开发提供一个嵌套。在很多情况下,测试一系列重载比找出可选参数的所有可能排列要容易得多@