11

I have an interface with default parameters and I want to call the implementing method from within the implementing class (in addition to from outside it). I also want to use its default parameters.

However, if I just call the method by name I cannot use the default parameters because they are only defined in the interface.I could repeat the default specifications in the implementing method but that is not likely due to DRY and all that details(especially the fact that the compiler would not check that they actually match up with the interface's defaults!)

I solve this by introducing a member called _this that is the same as this except it is declared as the interface type. Then when I want to use default parameters, I call the method with _this. Here is sample code:

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

        // ...
    }
}

Is there anything wrong with this pattern or a way to solve the problem in a better way? (Incidentally, I think this is a flaw in the language that I have to do something like this)

EDIT: Not a dup of Why are C# 4 optional parameters defined on interface not enforced on implementing class? ... I am primarily asking how to work around this language quirk, not asking why it was designed that way

Falco Alexander
  • 3,092
  • 2
  • 20
  • 39
JoelFan
  • 37,465
  • 35
  • 132
  • 205
  • 3
    Possible duplicate of [Why are C# 4 optional parameters defined on interface not enforced on implementing class?](http://stackoverflow.com/questions/4922714/why-are-c-sharp-4-optional-parameters-defined-on-interface-not-enforced-on-imple) – Giorgi Nakeuri May 10 '16 at 22:13
  • Related: http://stackoverflow.com/questions/2567163/optional-parameters-for-interfaces – Arturo Menchaca May 10 '16 at 22:14
  • You don't need to repeat the defaults, in my example you can notice how the explicit implementation is taking the interface parameters and in that explicit implementation I dont even have default values – bto.rdz May 11 '16 at 02:14
  • It doesn't make the question any less interesting, but putting parameter values on an interface bothers me deeply. To me, the value depends on the implementation, and therefore shouldn't be enforced by the interface. I think an overload would be much more appropriate. Still, that's an intriguing corner case – Kevin Gosse May 11 '16 at 06:45
  • @KooKiz, I kind of agree, but your concerns can be addressed by specifying all optional parameters in the interface as defaulting to `null` (using Nullable for value types). Then, each implementation can use `??` to default to an actual default... e.g. for `int? direction = null` in the interface, the implementation can start with `direction = direction ?? 90` to default to 90. – JoelFan May 11 '16 at 12:51

4 Answers4

5

You have three options.

Use an Extension Method as a Default

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

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

Explicitly Implement the Interface

This way you do not have to repeat the defaults defined in the IMovable interface, and the defaults for the interface and implementation can never go out of sync.

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

Repeat the Default Arguments

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

There could be two users of your code: one who uses only the IMovable interface, and one who uses only the Ball class. Arguably there could be an obscure scenario in which the defaults for moving an IMovable should be different than the defaults for moving a Ball, and neither user should have to care about the defaults they aren't looking at.

I concede this explanation is not very satisfying. If you want more info on why the language was designed this way, read the question and top answer Giorgi Nakeuri linked to in his comment on your question: Why are C# 4 optional parameters defined on interface not enforced on implementing class?

Community
  • 1
  • 1
Timothy Shields
  • 75,459
  • 18
  • 120
  • 173
  • It is not necessary to explicitly implement the interface... the compiler will recognize it even if I don't repeat the defaults – JoelFan May 11 '16 at 01:15
  • @JoelFan I'm aware of that. The salient point is that, if you do explicitly implement the interface, it is not possible for the interface defaults and the implementation defaults to get out of sync, simply because there are no implementation-specified defaults. – Timothy Shields May 11 '16 at 01:18
  • I'm saying that even without explicitly implementing the interface, I can leave out the defaults on the implementation... I don't see what explicitly implementing gains me – JoelFan May 11 '16 at 01:54
  • too many options, but definitively the explicit implementation should be the way – bto.rdz May 11 '16 at 02:19
3

Then use explicit implementation, when you cast and call it as the interface you will get the interface defaults, when you call it with the class you will get the class defaults eg:

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

And the output

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

bto.rdz
  • 6,636
  • 4
  • 35
  • 52
  • I can use *((Imovable)this)* everywhere even without explicitly implementing the interface, but that is what I am trying to avoid – JoelFan May 11 '16 at 01:16
  • @JoelFan But IMovable is an interface, it has no content or definition, it has no sense, you need to define the interface, an interface only indecates that certain object contains certain methods or properties, and no more – bto.rdz May 11 '16 at 01:22
  • @JoelFan IMovable says nothing but, hey you need to define this method, and that is what you are doing, it is the definition of an interface, I dont get why it is wrong at this point. – bto.rdz May 11 '16 at 01:35
1

You can explicit cast to the interface.

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

Output:

90 100

Or you can convert your interface to an abstract class.

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

Output:

90 100
Paulo Prestes
  • 484
  • 2
  • 8
  • 1
    That is what I am trying to avoid by using *_this* – JoelFan May 11 '16 at 01:13
  • 1
    If that is the case, why not convert the interface to an abstract class and implement the Move method with no parameters, that calls your move with your default parameters 90 and 100. – Paulo Prestes May 11 '16 at 01:19
  • Also, I think it is not a flaw in the in the language, because the default parameters are resolved in compile time, so the compiler can only be sure that you want to use the default parameter if you are using the interface type. – Paulo Prestes May 11 '16 at 01:43
  • 1
    I like the abstract class idea, but would prevent me from deriving from a more useful base class – JoelFan May 11 '16 at 02:06
-1

Because your method that you implemented is not exactly same with your interface, and your compiler doesn't know you want to implement that method.

Here are your answer.

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();
    }
}
Pajace
  • 15
  • 2
  • Not true! The compiler *does* consider it an implementation (without duplicating the defaults)! The proof is that I can call it through the interface. Whether I leave out the defaults, duplicate them or even change them (!!!), C# recognizes it as the implementation of the interface – JoelFan May 11 '16 at 01:11
  • Oh!! Thanks. By the way, if I don't explict args, it will get errors in Visual Studio. – Pajace May 14 '16 at 04:18