方法重载与可选参数在C#4.0中
哪一个更好? 一目了然可选参数似乎更好(less代码,lessXML文档等),但为什么大多数MSDN库类使用重载,而不是可选参数?
当您select使用可选参数(或重载)时,是否有任何特殊的事情需要注意?
在C#4.0中,“可选参数”与“命名参数”一起使用的一个很好的用例是,它向我们提供了一个方法重载的优雅替代方法,其中根据参数数量重载方法。
例如,假设你想要一个方法foo
被调用/使用像这样, foo()
, foo(1)
, foo(1,2)
, foo(1,2, "hello")
。 用方法重载你可以像这样实现解决scheme,
///Base foo method public void DoFoo(int a, long b, string c) { //Do something } /// Foo with 2 params only public void DoFoo(int a, long b) { /// .... DoFoo(a, b, "Hello"); } public void DoFoo(int a) { ///.... DoFoo(a, 23, "Hello"); } .....
使用C#4.0中的可选参数,您可以实现类似下面的用例,
public void DoFoo(int a = 10, long b = 23, string c = "Hello")
那么你可以使用像这样的方法 – 注意使用命名参数 –
DoFoo(c:"Hello There, John Doe")
此调用将参数值设为10,将参数b
设为23.此调用的另一种变体 – 注意,您不需要按照在方法签名中出现的顺序设置参数值,因为named参数会使值显式化。
DoFoo(c:"hello again", a:100)
使用命名参数的另一个好处是它大大增强了可读性,从而可以维护可选参数方法的代码。
注意在方法重载中,一种方法是多余的,不得不定义3个或更多的方法。 我发现这是使用可选参数和命名参数的一个很好的用例。
可选参数提供的问题,当您公开作为API公开。 重命名参数可能会导致问题。 更改默认值会导致问题(请参阅此处的一些信息: C#4.0可选参数的注意事项 )
另外,可选参数只能用于编译时常量。 比较一下:
public static void Foo(IEnumerable<string> items = new List<string>()) {} // Default parameter value for 'items' must be a compile-time constant
对此
public static void Foo() { Foo(new List<string>());} public static void Foo(IEnumerable<string> items) {} //all good
更新
这里有一些额外的阅读材料时,具有默认参数的构造函数不能很好地与reflection 。
这个几乎不用说,但是:
并非所有语言都支持可选参数。 如果你想让你的图书馆对这些语言友好,你必须使用重载。
诚然,这对大多数商店来说甚至都不是问题。 但是你可以打赌,这就是为什么微软不使用基类库中的可选参数。
我相信他们有不同的目的。 可选参数适用于何时可以使用参数的默认值,并且底层代码将相同:
public CreditScore CheckCredit( bool useHistoricalData = false, bool useStrongHeuristics = true) { // ... }
方法重载适用于具有互斥(参数的子集)的情况。 这通常意味着你需要对一些参数进行预处理,或者对于你的方法的不同“版本”你有不同的代码(注意,即使在这种情况下,一些参数也可以共享,这就是为什么我在上面提到“子集”的原因) :
public void SendSurvey(IList<Customer> customers, int surveyKey) { // will loop and call the other one } public void SendSurvey(Customer customer, int surveyKey) { ... }
(我前些时候在这里写过这个)
可选参数必须是最后一个。 所以你不能添加一个额外的参数,除非它也是可选的。 例如:
void MyMethod(int value, int otherValue = 0);
如果你想在不超载的情况下向这个方法添加一个新的参数,它必须是可选的。 喜欢这个
void MyMethod(int value, int otherValue = 0, int newParam = 0);
如果它不是可选的,那么你必须使用重载并删除'otherValue'的可选值。 喜欢这个:
void MyMethod(int value, int otherValue = 0); void MyMethod(int value, int otherValue, int newParam);
我假设你想要保持参数的顺序相同。
因此,使用可选参数可以减lessclass级中需要的方法的数量,但限制在于它们需要最后一个。
更新使用可选参数调用方法时,可以使用如下所示的命名参数:
void MyMethod(int value, int otherValue = 0, int newValue = 0); MyMethod(10, newValue: 10); // Here I omitted the otherValue parameter that defaults to 0
所以可选的参数给调用者更多的可能性。
最后一件事。 如果你用一个实现使用方法重载,像这样:
void MyMethod(int value, int otherValue) { // Do the work } void MyMethod(int value) { MyMethod(value, 0); // Do the defaulting by method overloading }
然后当像这样调用“MyMethod”时:
MyMethod(100);
将导致2个方法调用。 但是,如果使用可选参数,则只有一个“MyMethod”实现,因此只有一个方法调用。
两者都不是最好的。 他们都有自己的代码编写好的地方。 如果参数可以具有默认值,则应使用可选参数。 如果签名的差异超出了未定义可能具有默认值的参数(例如行为根据传递哪些参数以及哪些被保留为默认值而不同),则应该使用方法重载。
// this is a good candidate for optional parameters public void DoSomething(int requiredThing, int nextThing = 12, int lastThing = 0) // this is not, because it should be one or the other, but not both public void DoSomething(Stream streamData = null, string stringData = null) // these are good candidates for overloading public void DoSomething(Stream data) public void DoSomething(string data) // these are no longer good candidates for overloading public void DoSomething(int firstThing) { DoSomething(firstThing, 12); } public void DoSomething(int firstThing, int nextThing) { DoSomething(firstThing, nextThing, 0); } public void DoSomething(int firstThing, int nextThing, int lastThing) { ... }
使用可选参数的好地方是WCF,因为它不支持方法重载。
第三个选项是什么:传递一个类的实例,其中包含与各种“可选参数”相对应的属性。
这提供了与命名参数和可选参数相同的好处,但我觉得这通常更清晰。 它给你一个机会,在必要的时候(例如用组合)在逻辑上对参数进行分组,并封装一些基本的validation。
另外,如果你希望客户使用你的方法做任何types的元编程(比如构build涉及你的方法的linqexpression式),我认为保持方法签名简单有其优点。
这不是对原始问题的回答 ,而是对@NileshGule的回答的评论,但是:
a)我没有足够的声望点评论
b)多行代码在评论中很难阅读
Nilesh Gule写道:
使用可选参数的一个好处是,您不需要在方法中进行条件检查,如果input参数之一是string,如果string为空或为空。 由于分配给可选参数的默认值会在很大程度上降低防御性编码。
这实际上是不正确的,你仍然需要检查空值:
void DoSomething(string value = "") // Unfortunately string.Empty is not a compile-time constant and cannot be used as default value { if(value == null) throw new ArgumentNullException(); } DoSomething(); // OK, will use default value of "" DoSomething(null); // Will throw
如果您提供空string引用,则不会被默认值replace。 所以你仍然需要检查空值的input参数。
使用可选参数的一个好处是,您不需要在方法中进行条件检查,例如,如果input参数之一是string,则string为空或为空。 由于分配给可选参数的默认值会在很大程度上降低防御性编码。
命名参数提供了以任何顺序传递参数值的灵活性。
为了解决你的第一个问题,
为什么大多数MSDN库类使用重载而不是可选参数?
这是为了向后兼容。
在VS2010中打开C#2,3.0或3.5项目时,会自动升级。
想象一下,如果项目中使用的每个重载必须转换为与相应的可选参数声明相匹配,则会产生不便。
此外,俗话说“为什么修不破?”。 没有必要replace已经在新实现中工作的重载。