避免重复接口

时间:2016-05-10 22:02:44

标签: c#

有时候我有一个带默认参数的界面,我想从实现类 中调用实现方法(除了来自外部)。我也想使用它的默认参数。

但是,如果我只是按名称调用方法,则不能使用默认参数,因为它们仅在接口中定义。我可以在实现方法中重复默认规范,但这是不可取的,因为DRY和所有这些都需要(特别是编译器不会检查它们实际上是否与接口的默认值匹配!)

我通过引入一个名为_this的成员来解决这个问题,该成员与this相同,只是它被声明为接口类型。然后当我想使用默认参数时,我用_this调用方法。以下是示例代码:

public interface IMovable
{
    // I define the default parameters in only one place
    void Move(int direction = 90, int speed = 100);
}

public class Ball: IMovable
{
    // Here is my pattern
    private readonly IMovable _this;

    public Ball()
    {
        // Here is my pattern
        _this = this;
    }

    // I don't want to repeat the defaults from the interface here, e.g.
    // public void Move(int direction = 90, int speed = 100)
    public void Move(int direction, int speed)
    {
        // ...
    }

    public void Play()
    {
        // ...

        //This would not compile
        //Move();

        // Now I can call "Move" using its defaults
        _this.Move();

        // ...
    }
}

这种模式有什么问题或以更好的方式解决问题吗? (顺便说一下,我认为这是我必须做的事情的语言缺陷)

编辑:不是Why are C# 4 optional parameters defined on interface not enforced on implementing class?的重复...我主要是询问如何解决这种语言怪癖,而不是问为什么它是这样设计的

4 个答案:

答案 0 :(得分:4)

您有三种选择。

使用扩展方法作为默认值

public interface IMovable
{
    void Move(int direction, int speed);
}

public static MovableExtensions
{
    public static void Move(this IMovable movable)
    {
        movable.Move(90, 100);
    }
}

显式实现接口

这样您就不必重复IMovable接口中定义的默认值,接口和实现的默认值永远不会失去同步。

public class Ball : IMovable
{    
    void IMovable.Move(int direction, int speed)
    {
    }
}

重复默认参数

public class Ball : IMovable
{    
    public void Move(int direction = 90, int speed = 100)
    {
    }
}

您的代码可能有两个用户:一个只使用IMovable接口,另一个只使用Ball类。可以说可能存在一个模糊的场景,其中移动IMovable的默认值应该与移动Ball的默认值不同,并且用户都不应该关心他们没有看到的默认值

我承认这种解释并不令人满意。如果您想了解有关语言设计原因的更多信息,请阅读Giorgi Nakeuri在您对您的问题的评论中所链接的问题和最佳答案:Why are C# 4 optional parameters defined on interface not enforced on implementing class?

答案 1 :(得分:2)

然后使用显式实现,当你将其作为接口进行转换并将其调用为接口默认值时,当您使用该类调用它时,您将获得类默认值,例如:

public class Ball : IMovable
{
    //this uses the interface defaults
    //notice how you dont need to define the default values again
    //they are only specified once, in the interface definition
    void IMovable.Move(int direction, int speed)
    {
        Debug.WriteLine(direction + "," + speed);
    }

    //now for the specific case of this class you can have your own defaults
    //or none, just what ever fits your needs
    public void Move(int direction = 20, int speed = 10)
    {
        Debug.WriteLine(direction + ","+ speed);
    }

    public void Play()
    {
        Debug.WriteLine("From interface");
        ((IMovable) this).Move();
        Debug.WriteLine("From this class defaults");
        Move();
    }
}

输出

  

来自界面
    90,100
    从这个类默认为
    20,10

答案 2 :(得分:0)

您可以显式转换为界面。

using System;
using System.IO;
using System.Threading.Tasks;

public class Test {
    public static void Main()
    {
        var t = new Test1();
        t.Play();
    }

}
public interface IMovable
{
    // I define the default parameters in only one place
    void Move(int direction = 90, int speed = 100);
}

public class Test1 : IMovable{
    public void Move (int direction, int speed)
    {
        Console.Write($"{direction} {speed}");
    }

    public void Play (){
        ((IMovable)this).Move();
    }
}

输出:

90 100

或者您可以将您的界面转换为抽象类。

using System;
using System.IO;
using System.Threading.Tasks;

public class Test {
    public static void Main()
    {
        var t = new Test1();
        t.Play();
    }

}
public abstract class  IMovable
{
    // I define the default parameters in only one place
    public abstract void Move(int direction, int speed);

    public void Move(){
        this.Move(90, 100);
    }
}

public class Test1 : IMovable{

    public virtual void Move(int direction, int speed){

        Console.Write($"{direction} {speed}");
    }

    public void Play (){
        this.Move();
    }
}

输出:

90 100

答案 3 :(得分:-1)

因为您实现的方法与您的界面不完全相同,并且您的编译器不知道您想要实现该方法。

以下是你的答案。

public interface IMovable
{
    void Move(int direction = 90, int speed = 100);
}

public class Ball : IMovable
{
    // the method you want to implement from interface 
    // MUST same with interface's declaration
    public void Move(int direction = 90, int speed = 100)
    {
        // ...
    }

    public void Play()
    {
        Move();
    }
}