【问题标题】:Optional parameters for interfaces接口的可选参数
【发布时间】:2011-02-03 18:15:39
【问题描述】:

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

这可以编译,但看起来不正确。接口需要有可选参数,否则它不能正确反映在接口方法签名中。

我应该跳过可选参数而只使用可空类型吗?还是会按预期工作而没有副作用或后果?

【问题讨论】:

标签: c# interface c#-4.0 optional-parameters


【解决方案1】:

真正奇怪的是,您在界面中为可选参数设置的值实际上有所不同。我想您必须质疑该值是接口细节还是实现细节。我会说后者,但事情的表现就像前者。例如,以下代码输出 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);
        }
    }
}

Eric Lippert 就这个主题写了一个有趣的系列文章:Optional argument corner cases

【讨论】:

  • 默认值被编译到调用站点中,这就是为什么只使用您调用方法的类型中的值。
【解决方案2】:

您可以考虑使用 pre-optional-parameters 替代方案:

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);
    }
}

如果您不喜欢新语言功能的外观,则不必使用它。

【讨论】:

  • 请随时提供有关其工作原理的其他说明,或者提供指向您所说的“可选参数替代方案”的链接。可能会帮助未来的用户! :]
  • 当然,这样做的真正老方法(在 C# 3 之前引入扩展方法)是在类和接口中使用方法重载。如果您可以访问类代码,重载可能比扩展方法更好,因为它将代码保存在一个地方。
【解决方案3】:

您不必在实现中将参数设为可选。你的代码会更有意义:

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

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

这样,默认值是明确的。事实上,我很确定实现中的默认值不会有任何影响,因为接口为其提供了默认值。

【讨论】:

  • 如果您的引用是使用类而不是接口键入的,则实现中的默认值将产生影响。
【解决方案4】:

这样的事情呢?

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 */ }
}

【讨论】:

【解决方案5】:

要考虑的是在使用 Mocking 框架时会发生什么,这些框架基于接口的反射工作。 如果在接口上定义了可选参数,则将根据接口中声明的内容传递默认值。 一个问题是,没有什么能阻止您在定义上设置不同的可选值。

【讨论】:

    【解决方案6】:

    我也可以这样建议:​​

    public interface IFoo
    {
          void Bar(int i);
          void Bar(int i, int j);
    }
    
    public class Foo
    {
         // when "j" has default value zero (0).
         void Bar(int i)
         {
             Bar(i, 0);
         }
         
         void Bar(int i, int j) {}
    }
    

    【讨论】:

      猜你喜欢
      • 2017-10-18
      • 1970-01-01
      • 2018-09-06
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-06
      相关资源
      最近更新 更多