接口的可选参数


69

使用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在下面的评论中给出了各种陷阱的详细示例,我希望编译器将标记不匹配的默认参数。就是说,我在代码中使用了它,因为它确实表明了我在界面和实现级别上的开发意愿,供其他开发人员参考。
Pete Magsig 2014年

Answers:


30

您可以考虑使用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);
    }
}

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


44
但是...这是新的!和闪亮!:-)
bryanjonker

1
请随意提供有关其工作原理的其他说明,或者可能提供指向您所称为的“ pre-optional-paremeters替代品”的链接。可能会帮助将来的用户!:]
Danny Bullis

1
当然,这样做的真正老方法(在C#3之前引入扩展方法)是在类和接口中都使用方法重载。如果可以访问类代码,无论如何,重载可能比扩展方法要好,因为它可以将代码保存在一个地方。
马丁·布朗

60

真正奇怪的是,您在接口中为可选参数输入的值实际上有所不同。我想您必须质疑该值是接口详细信息还是实现详细信息。我会说后者,但事情表现得像前者。例如,以下代码输出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)就这个确切的话题写了一个有趣的系列文章:可选的论点角落案例


4

您无需在实现中将参数设为可选。然后,您的代码会更有意义:

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

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

这样,默认值是明确的。实际上,我非常确定实现中的默认值不会起作用,因为接口为其提供了默认值。


6
如果使用类而不是接口键入您的引用,则实现中的默认值将起作用。
马丁·布朗


2

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.