Универсальные методы: как заставить использовать самый специализированный метод

Я определил универсальный метод 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 ДЕЙСТВИТЕЛЬНО есть, и запустить соответствующий метод. Вот так:

  1. Какой тип делает i1 иметь? ImplementsChildInterface Хорошо, тогда я посмотрю на методы там.
  2. Какой тип делает 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)"

который напечатает желаемый результат.

Другие вопросы по тегам