自从C#添加了可选参数,使用可选参数还是方法重载被认为是更好的实践方式?还是在特定情况下,你会更倾向于使用其中一种?例如,一个有很多参数的函数是否更适合使用可选参数?
自从C#添加了可选参数,使用可选参数还是方法重载被认为是更好的实践方式?还是在特定情况下,你会更倾向于使用其中一种?例如,一个有很多参数的函数是否更适合使用可选参数?
可选参数很好,但应在有意义时使用。可选参数经常会混淆方法的意图--如果有其他替代方案,我会倾向于选择替代方案。
可选参数和命名参数的需求部分是因为COM允许可选和命名参数:
MSDN
一些API,尤其是Office自动化API等COM接口,专门针对命名和可选参数编写。直到现在,从C#调用这些API非常痛苦,必须显式传递多达30个参数,其中大多数具有合理的默认值,可以省略。
来自forums.asp.net的SomeNewKid简明地表达了这一点:
http://forums.asp.net/t/386604.aspx/1
过载方法通常比可选参数更可取。为什么呢?为了保持每个方法的清晰目的。也就是说,每个方法应该做一件事情得当。一旦您引入可选参数,就会稀释该方法的清洁度,并引入分支逻辑,最好将其保留在方法之外。当您开始使用继承时,这种目的的清晰度变得更加重要。如果您覆盖具有一个或多个可选参数的方法,则它们变得更难处理。因此,我建议除了快速和肮脏的类之外,您优先使用重载而不是可选参数。请记住,可选参数是一种语法糖:
Reflector C#:
public class Class1
{
// Methods
public Class1()
{
this.Method1("3", "23");
}
public void Method1(string one, [Optional, DefaultParameterValue("23")] string two)
{
}
}
IL:
.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
}
}
在Visual Studio和FxCop中的代码分析建议您不要在公共API中使用可选参数 (规则CA1026:不应使用默认参数)。原因是并非所有.NET语言都支持它们。
我认为更好的避免使用它们的原因是,如果您在API 2.0版本中添加更多的重载,它们可能会引入运行时或编译时问题。Phil Haack在这里解释了。
我将采用Phil的结论:可选参数是为了支持COM互操作而设计的;如果您不使用COM,请不要使用它们。
我不确定这个问题是否有标准答案 - 这是主观的,需要根据具体情况而定。然而,在我的观点中,可选参数创造了更加明确的API,因此我通常比起方法重载更喜欢可选参数。
具体地说,当使用Intellisense时,我更喜欢看到这样的形式:
而不是这样:
如果我不指定参数1和参数2的值,我可能需要猜测(或查找文档)。
可选参数旨在促进COM对象的交互,因为COM对象使用大量可选参数。因此,如果您正在进行P/Invoke或COM对象处理,请优先考虑使用可选参数。否则,方法重载是正确的选择,因为它可以节省很多混淆。
与其使用过载或命名可选参数,我更喜欢对象初始化器。在类中,你只需要一个无参构造函数和想要在初始化时设置的公共属性。
假设类Bar有公共字符串属性"Name"和"Pet",你可以像这样构造一个新对象。
var foo = new Bar { Name = "Fred", Pet = "Dino" };
可选参数需要一个默认值(我只是假设),因此在某些情况下可能很难提供。为什么呢?因为有些类需要运行时信息才能初始化,而这些信息可能在编译器中不可用。
当涉及到原始类型时,答案可能与是否可以假定默认值有关,或者如果缺少参数可能意味着方法的不同行为。