Универсальные методы: как заставить использовать самый специализированный метод
Я определил универсальный метод Use<T>
в интерфейсе IInterface
, Я попытался сделать реализацию этого интерфейса, где конкретная реализация Use<T>
метод зависит от фактического типа T
и я хочу всегда вызывать самый специализированный метод. Но это не работает:
interface IInterface { void Use<T>(T other) where T : IInterface; }
interface IChildInterface : IInterface { }
class ImplementsIInterface : IInterface
{
public void Use<T>(T other) where T : IInterface
{
Debug.WriteLine("ImplementsInterface.Use(IInterface)");
}
}
class ImplementsChildInterface : IChildInterface
{
public void Use<T>(IChildInterface other) where T : IInterface
{ // idea: if other is IChildInterface, use this method
Debug.WriteLine("ImplementsChildInterface.Use(IChildInterface)");
}
public void Use<T>(T other) where T : IInterface
{ // idea: if above method is not applicable, use this method
Debug.WriteLine("ImplementsChildInterface.Use(IInterface)");
}
}
Вот мой основной метод:
public static void Main()
{
IChildInterface childinterf = new ImplementsChildInterface();
childinterf.Use(new ImplementsChildInterface()); // outputs "ImplementsChildInterface.Use(IInterface)"
// but should output "ImplementsChildInterface.Use(IChildInterface)"
childinterf.Use(new ImplementsIInterface()); // outputs "ImplementsChildInterface.Use(IInterface)"
}
Метод, который принимает IChildInterface
аргумент никогда не вызывается, хотя и должен.
Есть ли способ сделать эту работу? Или мой подход в корне неверен?
Обратите внимание, что это необходимость IInterface
имеет только одно определение метода. Я мог бы расширить иерархию интерфейса в любое время (и, таким образом, увеличить количество реализаций, которые я мог бы предоставить в классе реализации), но это не должно приводить к необходимости добавлять больше определений методов в IInterface
, В противном случае весь смысл использования интерфейсов (т. Е. Гибкости) будет упущен.
Все ответы, которые я получил до сих пор, касаются необходимости разыгрывать. Это также то, что я не хочу делать, так как это делает всю установку бесполезной. Позвольте мне объяснить более широкую картину того, чего я пытаюсь достичь:
Давайте представим, что мы создали какой-то экземпляр IInterface
(вот так: IInterface foo = new ImplementsChildInterface();
). Он будет вести себя определенным образом, но он всегда будет вести себя одинаково - независимо от того, рассматриваем ли мы его как IInterface
, IChildInterface
или ImplementsChildInterface
, Потому что, если мы вызовем какой-либо метод для него, компилятор (или среда выполнения? Я не знаю) проверит, какой тип он действительно, и запустит метод, определенный в этом типе.
Теперь представьте, что у нас есть два экземпляра i1
а также i2
из IInterface
, Они снова, под капотом, конкретные реализации IInterface
Таким образом, они имеют конкретное поведение, независимо от того, через какие очки мы им кажемся.
Поэтому, когда я бегу i1.Use(i2)
, компилятор (или время выполнения?) должен быть в состоянии выяснить, что i1
а также i2
ДЕЙСТВИТЕЛЬНО есть, и запустить соответствующий метод. Вот так:
- Какой тип делает
i1
иметь?ImplementsChildInterface
Хорошо, тогда я посмотрю на методы там. - Какой тип делает
i2
иметь?ImplementsIInterface
ок, тогда посмотрим, существует ли методUse(ImplementsIInterface ...)
, Нет, но, может быть, есть запасной вариант?ImplementsIInterface
этоIInterface
Итак, давайте посмотрим, существует ли методUse(IInterface ...)
, Да, он существует, так что давайте назовем это!
1 ответ
Ни IInterface
ни IChildInterface
есть член Use<T>(IChildInterface other)
определяется, но только Use<T>(T other)
,
Твой класс ImplementsChildInterface
на другой стороне есть метод Use<T>(IChildInterface other)
, Как вы заявляете childInterf
как ссылка типа IChildInterface
Вы не можете получить доступ к этому члену, кроме тех, которые определены в интерфейсе. Таким образом, вы должны привести к фактическому классу, чтобы получить доступ к методу, принимающему экземпляр IChildInterface
, Но даже тогда используется общая реализация. Таким образом, вы должны также привести ваш параметр к IchildInterface
:
ImplementsChildInterfacechildinterf = new ImplementsChildInterface();
childinterf.Use(((IChildInterface)new ImplementsChildInterface());
childinterf.Use(new ImplementsIInterface());
Кроме того, поскольку вы не используете универсальный параметр типа в вашем более специализированном методе, вы также можете его опустить:
class ImplementsChildInterface : IChildInterface
{
public void Use(IChildInterface other)
{ // idea: if other is IChildInterface, use this method
Debug.WriteLine("ImplementsChildInterface.Use(IChildInterface)");
}
public void Use<T>(T other) where T : IInterface
{ // idea: if above method is not applicable, use this method
Debug.WriteLine("ImplementsChildInterface.Use(IInterface)");
}
}
В качестве альтернативы вы также можете добавить метод в свой IChildInterface
:
void Use<T>(IChildInterface other) where T : IChildInterface;
Теперь вы можете использовать
IChildInterface childinterf = new ImplementsChildInterface();
childinterf.Use<IChildInterface>(new ImplementsChildInterface()); // outputs "ImplementsChildInterface.Use(IInterface)"
который напечатает желаемый результат.