Объявите интерфейс, в котором конкретная реализация имеет конкретные типы
public interface ISomeInterface
{
IOut SomeMethod(IIn aIn)
}
public class MyOut : IOut
{
public string AnExtraProp {get; set;}
}
public class MyIn : IIn
{
public string AnotherExtraProp {get; set;} }
}
public class MyConcreteOfSomeInterface : ISomeInterface
{
public MyOut SomeMethod(MyIn aIn)
{
}
}
Можно ли иметь много классов (например, MyConcreteOfSomeInterface, MyConcrete2OfSomeInterface,....) для реализации интерфейса (например, ISomeInterface), но при этом иметь параметры конкретного типа (например, MyIn, MyOut и т. Д.).
Я понимаю, что могу заявить:
public interface ISomeInterface<TIn, TOut>
{
TOut SomeMethod(TIn aIn)
}
но поскольку ISomeInterface будет иметь много методов, это не будет практичным. Скажем, мне нужно добавить дополнительные методы SomeMethod2 и SomeMethod3, тогда я получу:
public interface ISomeInterface<TIn, TOut, TIn2, TOut2, TIn3, TOut3>
{
TOut SomeMethod(TIn aIn)
TOut2 SomeMethod(TIn2 aIn)
TOut3 SomeMethod(TIn3 aIn)
}
поэтому декларация становится громоздкой довольно быстро.
Какой шаблон дизайна я могу использовать для достижения:
- Многие конкретные классы, реализующие интерфейс ISomeInterface AND
- Используя конкретные параметры / возвращаемые значения, которые реализуют необходимые интерфейсы IIn, IOut?
В ISomeInteface будет много методов с различными типами для комбинации параметров / интерфейса.
1 ответ
Давайте упростим проблему. Предположим, у нас есть:
class Animal {}
class Giraffe : Animal {}
interface IFoo
{
Animal M();
}
Можем ли мы тогда иметь
class C : IFoo
{
public Giraffe M() => new Giraffe();
}
К сожалению нет. Реализация интерфейса должна точно соответствовать.
Теперь вы можете подумать: "Эй, интерфейс требует, чтобы животное было возвращено, а я возвращаю животное, а именно жирафа, так в чем же проблема?"
Ответ в том, что проблем нет. C# может иметь систему типов, где это работает, и эта функция была предложена много-много раз. Это называется "ковариация возвращаемого типа", и если вы выполните поиск здесь, вы найдете много вопросов об этом.
Однако C# не имеет этой функции, и поэтому вам не повезло. Лучшее, что вы можете сделать, это:
class C : IFoo
{
Animal IFoo.M() => this.M();
public Giraffe M() => new Giraffe();
}
И теперь ты в порядке. IFoo
Контракт явно реализован, а открытая поверхность класса имеет более конкретную подпись.
Точно так же, что если бы мы имели:
interface IBar()
{
void N(Giraffe g);
}
Это не законно:
class D : IBar
{
public void N(Animal g) { ... }
}
Опять же, это было бы совершенно разумно. IBar требует, чтобы DN был вещью, которой вы можете передать жирафа, а DN - вещь, которой вы можете передать жирафа или любое животное. Но опять же, C# не поддерживает эту функцию. Это называется формальной противоположностью параметров, и очень небольшое количество языков программирования поддерживает ее.
Выполните поиск на C# ковариации и контравариантности для получения подробной информации о том, какие виды дисперсии поддерживаются в C#.
Также обратите внимание, что это не будет безопасным для типов:
interface IBaz
{
void P(Animal a);
}
class E : IBaz
{
public void P(Giraffe g) { }
}
Потому что вы должны быть в состоянии сказать ((IBaz)(new E())).P(new Tiger())
, IBaz
говорит, что реализация должна быть в состоянии принять любое животное, поэтому вы не можете реализовать его с помощью метода, который принимает только жирафов. Логически было бы безопасно, чтобы возвращаемые типы становились более конкретными, но формальные типы параметров должны становиться менее конкретными. Вот почему это обратная дисперсия типа возврата, но контрастная дисперсия формального параметра, потому что направление преобразования изменяется в противоположном случае.