使用C#,我们现在可以具有可选参数,并为它们提供default值,如下所示:
public abstract class ImporterBase : IImporter {
public void ImportX(bool skipId = true) {
//....
}
}
现在,假设在我们派生的接口中,我们有
public interface IImporter {
void ImportX(bool skipId = false);
}
请参见,默认值在基类中定义为与在接口中不同。这确实令人困惑,因为现在默认值取决于我是否执行
IImporter interfaceImporter = new myConcreteImporter(); //which derives from ImporterBase
interfaceImporter.DoX(); //skipId = false
或
ImporterBase concreteImporter = new myConcreteImporter(); //which derives from ImporterBase
concreteImporter.DoX(); //skipId = true
为什么允许在接口和派生类中以不同的方式定义默认值?
注意:this question similar, but focuses on the optionality, not on the value.
有充分的理由。参见here。
简短的答案是,如果将可选值作为方法签名的一部分,则会引起一些问题。想象下面的代码:
public abstract class A
{
public abstract void SomeFunction(bool flag);
}
public class B : A
{
public override void SomeFunction(bool flag = true)
{
//do something
Console.WriteLine(flag);
}
}
如果可选参数值是方法签名的一部分,则由于A在方法签名中没有bool flag = true,我会收到编译错误。当然,这是一个简单的修复程序,但是如果您将A交付给第三方并使用其自定义代码创建了类B,则他们必须更改代码以具有可选参数。还请记住,当存在多个继承级别时,这种情况会加剧。因此,最简单的解决方法是,出于这些目的,不要将可选参数值视为方法签名的一部分。
为了澄清,我将问题解释为:
如果接口/基类中定义了一个方法,该方法具有一个带有带有默认值的参数的方法,并且一个类实现/覆盖了该方法,但提供了一个不同默认值,为什么不编译器警告?
注意,这不包括实现不提供默认值的情况[-由Eric Lippert解释。
我问了这个on the csharplang gitter channel,长期以来一直参与语言设计的人的回答是:
我认为分析仪听起来很不错。从那开始,以及此处发布的其他链接(甚至都没有提到这个特定案例),我最好的猜测是,这个特殊案例只是未被考虑,或者被短暂考虑,但被认为太小众。当然,一旦发布了C#4,就无法在不破坏向后兼容性的情况下添加编译器错误或警告。
[您可以编写一个分析器来捕获这种情况(它具有修正默认值的代码修补程序,然后尝试将其合并到Roslyn中。
接口更改其参数之一的默认值
两个具有不同默认值的接口
interface I1
{
void Foo(bool x = false);
}
interface I2
{
void Foo(bool x = true);
}
class C : I1, I2
{
...?
}
如果您确实想为C.Foo
指定默认值,则可以通过显式实现接口之一来解决这种情况:
class C : I1, I2
{
public void Foo(bool x = false) { ... }
void I2.Foo(bool x) => Foo(x);
}
或者,您可以忽略这种情况,而不发出警告。
在子类中添加接口
interface I1
{
void Foo(bool x = false);
}
class Parent
{
public void Foo(bool x = true) { ... }
}
class Child : Parent, I1
{
...?
}
我不确定这将是一个直观的解决方案,但是由于它是如此的小众,所以我很想忽略它,而不发出警告。