簡體   English   中英

C# 可選參數:為什么我可以在接口和派生類上定義默認不同?

[英]C# optional parameters: Why can I define the default different on interface and derived class?

使用 C#,我們現在可以擁有可選參數,並為它們提供如下默認值:

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

為什么允許在接口和派生類中定義不同的默認值?

注意: 這個問題類似,但側重於可選性,而不是價值。

這是有充分理由的。 這里

簡短的回答是,如果它將可選值視為方法簽名的一部分,則會導致一些問題。 想象一下下面的代碼:

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解釋。


我在csharplang gitter 頻道上問過這個問題,長期參與語言設計的人的回答是:

我認為分析儀聽起來非常適合這個。

從那個,以及這里發布的其他鏈接(甚至沒有提到這個特定案例),我最好的猜測是這個特定案例沒有被考慮,或者被簡單考慮但被認為太小眾而被駁回。 當然,一旦 C# 4 發布,就無法在不破壞向后兼容性的情況下添加編譯器錯誤或警告。

你可以編寫一個分析器來捕捉這種情況(它有一個代碼修復來糾正默認值),並嘗試將它合並到 Roslyn 中。


作為腳注,我可以看到一些會導致問題的情況。

接口更改其參數之一的默認值

這已經是一個二進制破壞性更改,這將使其成為一個源破壞性更改。

具有不同默認值的兩個接口

interface I1
{
    void Foo(bool x = false);
}
interface I2
{
    void Foo(bool x = true);
}
class C : I1, I2
{
   ...?
}

如果您確實想為C.Foo指定默認值, 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
{
    ...?
}

我不確定對此的直觀解決方案是什么,但由於它是如此小眾,我很想忽略它,而不是警告。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM