C#可选参数:为什么可以在接口和派生类上定义默认的不同?

问题描述 投票:3回答:2

使用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.

c# interface optional-parameters derived-class
2个回答
1
投票

有充分的理由。参见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,则他们必须更改代码以具有可选参数。还请记住,当存在多个继承级别时,这种情况会加剧。因此,最简单的解决方法是,出于这些目的,不要将可选参数值视为方法签名的一部分。


0
投票

为了澄清,我将问题解释为:

如果接口/基类中定义了一个方法,该方法具有一个带有带有默认值的参数的方法,并且一个类实现/覆盖了该方法,但提供了一个不同默认值,为什么不编译器警告?

注意,这不包括实现不提供默认值的情况[-由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 { ...? }

我不确定这将是一个直观的解决方案,但是由于它是如此的小众,所以我很想忽略它,而不发出警告。
© www.soinside.com 2019 - 2024. All rights reserved.