2010-04-02 67 views
55

使用c#4.0 - 構建一個接口和一個實現該接口的類。我想在接口中聲明一個可選參數並將其反映到類中。所以,我有以下幾點:接口的可選參數

public interface IFoo 
{ 
     void Bar(int i, int j=0); 
} 

public class Foo 
{ 
     void Bar(int i, int j=0) { // do stuff } 
} 

這個編譯,但它看起來不正確。該接口需要具有可選參數,否則它在接口方法簽名中不能正確反映。

我應該跳過可選參數,只使用可空類型嗎?或者這會按預期工作,沒有副作用或後果?

+2

Martin的評論下面給出了各種陷阱的詳細例子,我希望編譯器標記不匹配的默認參數。也就是說,我在我的代碼中使用了它,因爲它確實表明了我作爲開發人員在接口和實現級別爲其他開發人員查看的意圖。 – 2014-07-09 15:13:15

+0

有趣的相關問題,「[是否有任何理由在接口中聲明可選參數?](https:// stackoverflow。com/questions/6752762 /)「,以及」[爲什麼C#4可選參數在接口上沒有被強制實施類?](https://stackoverflow.com/questions/4922714/)「,第二個有趣的[答案來自Lippert](https://stackoverflow.com/a/4923642/1028230)。 – ruffin 2017-09-07 16:55:41

回答

28

你可以考慮預先可選參數的選擇:

public interface IFoo 
{ 
    void Bar(int i, int j); 
} 

public static class FooOptionalExtensions 
{ 
    public static void Bar(this IFoo foo, int i) 
    { 
     foo.Bar(i, 0); 
    } 
} 

如果你不喜歡一個新的語言特性的外觀,你不必使用它。

+40

但是...它是新的!閃亮!:-) – bryanjonker 2010-04-02 14:52:03

+1

隨意提供如何工作,或者也許是一個額外的解釋鏈接到你所指的「預備選擇 - 備選方案替代方案」。可能會幫助未來的用戶!:] – 2016-10-21 17:22:03

+0

當然,真正舊式的做法(在C#3前引入擴展方法)是在兩種方法中使用方法重載類和接口如果你有權訪問類代碼重載可能比擴展方法更好,因爲它將代碼保存在一個地方 – 2017-03-02 12:34:58

1

放在一邊,那將完全聽起來像你想要完成的。

+2

這不能提供問題的答案要批評或要求作者澄清,留下紀念nt低於他們的帖子。 – 2014-04-16 14:23:40

+3

我不知道。在我看來,這是對所問問題的直接回答:「或者這會按預期工作,沒有副作用或後果?」 – MojoFilter 2015-02-25 14:05:14

3

這樣的事情呢?

public interface IFoo 
{ 
    void Bar(int i, int j); 
} 

public static class IFooExtensions 
{ 
    public static void Baz(this IFoo foo, int i, int j = 0) 
    { 
     foo.Bar(i, j); 
    } 
} 

public class Foo 
{ 
    void Bar(int i, int j) { /* do stuff */ } 
} 
+0

@Iznogood - 你在這裏批准的編輯顯然不是一個有效的編輯:http://stackoverflow.com/review/suggested-edits/1041521。查看編輯時請更加小心。 – LittleBobbyTables 2012-11-19 16:10:51

44

真奇怪的是,在界面中爲可選參數放置的值實際上有所不同。我想你必須質疑這個值是一個接口細節還是一個實現細節。我會說後者,但事情就像前者一樣。例如,以下代碼輸出1 0 2 5 3 7。

// Output: 
// 1 0 
// 2 5 
// 3 7 
namespace ScrapCSConsole 
{ 
    using System; 

    interface IMyTest 
    { 
     void MyTestMethod(int notOptional, int optional = 5); 
    } 

    interface IMyOtherTest 
    { 
     void MyTestMethod(int notOptional, int optional = 7); 
    } 

    class MyTest : IMyTest, IMyOtherTest 
    { 
     public void MyTestMethod(int notOptional, int optional = 0) 
     { 
      Console.WriteLine(string.Format("{0} {1}", notOptional, optional)); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyTest myTest1 = new MyTest(); 
      myTest1.MyTestMethod(1); 

      IMyTest myTest2 = myTest1; 
      myTest2.MyTestMethod(2); 

      IMyOtherTest myTest3 = myTest1; 
      myTest3.MyTestMethod(3); 
     } 
    } 
} 

是什麼樣有趣的是,如果你的界面使參數可選類實現它沒有這樣做:

// Optput: 
// 2 5 
namespace ScrapCSConsole 
{ 
    using System; 

    interface IMyTest 
    { 
     void MyTestMethod(int notOptional, int optional = 5); 
    } 

    class MyTest : IMyTest 
    { 
     public void MyTestMethod(int notOptional, int optional) 
     { 
      Console.WriteLine(string.Format("{0} {1}", notOptional, optional)); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyTest myTest1 = new MyTest(); 
      // The following line won't compile as it does not pass a required 
      // parameter. 
      //myTest1.MyTestMethod(1); 

      IMyTest myTest2 = myTest1; 
      myTest2.MyTestMethod(2); 
     } 
    } 
} 

什麼似乎是一個錯誤,但是,如果你明確地實現了接口,你在類中爲可選值給出的值是毫無意義的。在以下示例中,如何使用值9?編譯時甚至沒有提示。

// Optput: 
// 2 5 
namespace ScrapCSConsole 
{ 
    using System; 

    interface IMyTest 
    { 
     void MyTestMethod(int notOptional, int optional = 5); 
    } 

    class MyTest : IMyTest 
    { 
     void IMyTest.MyTestMethod(int notOptional, int optional = 9) 
     { 
      Console.WriteLine(string.Format("{0} {1}", notOptional, optional)); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyTest myTest1 = new MyTest(); 
      // The following line won't compile as MyTest method is not available 
      // without first casting to IMyTest 
      //myTest1.MyTestMethod(1); 

      IMyTest myTest2 = new MyTest();    
      myTest2.MyTestMethod(2); 
     } 
    } 
} 
2

您不必在實現中使參數爲可選參數。你的代碼會更有意義:

public interface IFoo 
{ 
     void Bar(int i, int j = 0); 
} 

public class Foo 
{ 
     void Bar(int i, int j) { // do stuff } 
} 

這樣,它的默認值是明確的。實際上,我很確定實現中的默認值不起作用,因爲接口爲它提供了默認值。

+2

如果您的引用是使用類而不是接口鍵入的,則實現中的默認值將會有效。 – 2017-03-02 12:40:30

2

需要考慮的事情是當使用基於接口反射的Mocking框架時發生的情況。 如果在接口上定義了可選參數,則將根據接口中聲明的內容傳遞默認值。 一個問題是,沒有什麼能阻止你在定義上設置不同的可選值。