关于c#:避免重复接口的默认值


Avoid repeating the defaults of an interface

有时我有一个带有默认参数的接口,我想从实现类内部(除了从实现类外部)调用实现方法。我还想使用它的默认参数。

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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();

        // ...
    }
}

这种模式有什么问题吗,或者用更好的方法解决问题?(顺便说一句,我认为这是语言上的一个缺陷,我必须这样做)

编辑:不重复为什么在接口上定义的C 4可选参数没有在实现类上强制执行?…我主要是问如何解决这种语言怪癖,而不是问为什么它是这样设计的。


你有三个选择。

使用扩展方法作为默认值

1
2
3
4
5
6
7
8
9
10
11
12
public interface IMovable
{
    void Move(int direction, int speed);
}

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

号显式实现接口

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

1
2
3
4
5
6
public class Ball : IMovable
{    
    void IMovable.Move(int direction, int speed)
    {
    }
}

重复默认参数

1
2
3
4
5
6
public class Ball : IMovable
{    
    public void Move(int direction = 90, int speed = 100)
    {
    }
}

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

我承认这个解释不太令人满意。如果你想要更多关于语言为什么是这样设计的信息,请阅读问题和答案:为什么在接口上定义的C 4可选参数在实现类上没有强制执行?


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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();
    }
}

以及输出

From Interface
90, 100
From this class defaults
20, 10


可以显式转换到接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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();
    }
}

输出:

1
90 100

或者可以将接口转换为抽象类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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();
    }
}

输出:

1
90 100


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

这是你的答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();
    }
}