Избегайте повторения значений по умолчанию интерфейса
Иногда у меня есть интерфейс с параметрами по умолчанию, и я хочу вызвать метод реализации из внутри класса реализации (в дополнение к нему извне). Я также хочу использовать его параметры по умолчанию.
Однако, если я просто вызываю метод по имени, я не могу использовать параметры по умолчанию, потому что они определены только в интерфейсе. Я мог бы повторить спецификации по умолчанию в методе реализации, но это нежелательно из-за СУХОЙ и все, что влечет за собой (особенно тот факт, что компилятор не проверял, что они действительно соответствуют настройкам по умолчанию!)
Я решаю это, введя член с именем _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();
// ...
}
}
Есть ли что-то не так с этим шаблоном или способом решить проблему лучше? (Кстати, я думаю, что это недостаток в языке, который я должен сделать примерно так)
РЕДАКТИРОВАТЬ: Не дублировать Почему дополнительные параметры С# 4, определенные на интерфейсе, не выполняются при реализации класса?... Я прежде всего спрашивая, как обойти эту языковую причуду, а не спрашивать, почему она была разработана именно так.
Ответы
Ответ 1
У вас есть три варианта.
Использовать метод расширения по умолчанию
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 в своем комментарии к вашему вопросу: Почему существуют дополнительные параметры С# 4, определенные на интерфейсе не выполняются при реализации класса?
Ответ 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
Из этого класса defaults
20, 10
Ответ 3
Вы можете явно указать на интерфейс.
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
Ответ 4
Поскольку ваш метод, который вы внедрили, не совсем совпадает с вашим интерфейсом, и ваш компилятор не знает, что вы хотите реализовать этот метод.
Вот ваш ответ.
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 declaration
public void Move(int direction = 90, int speed = 100)
{
// ...
}
public void Play()
{
Move();
}
}