Полиморфизм через методы расширения?

У меня есть библиотека классов, которые содержат некоторые базовые классы и другие, полученные из них. В этой библиотеке классов я использую полиморфизм, чтобы делать то, что хочу. Теперь в потребляющем приложении я хочу изменить поведение некоторого кода на основе типа времени выполнения дочерних классов. Поэтому предположим следующее:

public class Base { }
public class Child1 : Base { }
public class Child2 : Base { }

Теперь в потребляющем приложении я хочу сделать что-то следующим образом (обратите внимание, что все следующие классы находятся в приложении-потребителе и не могут быть указаны в библиотеке классов):

public interface IMyInterface1 { }
public interface IMyInterface2 { }
public static class Extensions
{
    public static void DoSomething(this Base myObj, Object dependency)
    {

    }

    public static void DoSomething(this Child1 myObj, Object dependency)
    {
        IMyInterface1 myInterface = dependency as IMyInterface1;
        if (myInterface != null)
        {
            //Do some Child1 specific logic here
        }
    }

    public static void DoSomething(this Child2 myObj, Object dependency)
    {
        IMyInterface2 myInterface = dependency as IMyInterface2;
        if (myInterface != null)
        {
            //Do some Child2 specific logic here
        }
    }
}

UPDATE:

Это не работает. Он всегда вызывает метод расширения базового класса. Есть ли другой способ, который позволит мне сделать это и избежать необходимости явно проверять тип выполнения? Причиной является то, что можно добавить больше классов, полученных из Base, и соответствующие методы расширения могут исходить из какой-либо другой внешней сборки.

Спасибо заранее.

Ответ 1

Как уже говорилось в @SLaks, вы не можете вызывать метод как метод расширения (даже с типом dynamic)... однако вы можете вызвать статический метод с типом dynamic

Итак, хотя это не удастся

Base base1 = new Child1();
(base1 as dynamic).DoSomething();

Это будет работать

Base base1 = new Child1();
Extensions.DoSomething(base1 as dynamic);

Ответ 2

Нет, это не сработает.

Методы расширения статически отправляются с использованием того же механизма, что и разрешение перегрузки.

Если у вас есть переменная типа времени компиляции Base, компилятор всегда будет вызывать базовый метод расширения, независимо от типа среды выполнения.

Вместо этого вы можете сделать базовый метод расширения проверкой типа времени выполнения и вызвать соответствующий другой метод расширения.

Ответ 3

Я искал то же самое сейчас.

Вы можете добавить еще один метод к вашему классу расширения следующим образом:

public static void DoSomething(this Base myObj, Object dependency)
{       
    if(myObj.IsSubclassOf(Base))
    {
      // A derived class, call appropriate extension method.  
      DoSomething(myObj as dynamic, dependency);
    }
    else
    {
        // The object is Base class so handle it.
    }
} 

Вам не нужна проверка if/else, если базовый класс является абстрактным (или никогда не используется в дикой природе):

public static void DoSomething(this Base myObj, Object dependency)
{ 
    DoSomething(myObj as dynamic, dependency);
}

[Edit] На самом деле это не будет работать в вашем случае, так как вы не реализуете поддержку всех производных объектов (так что все равно можно получить бесконечную рекурсию). Я думаю, вы могли бы передать что-то, чтобы проверить рекурсию, но данный ответ является самым простым. Я оставлю это здесь, поскольку это может вызвать больше идей.

Ответ 4

Ниже приведен минимальный пример, показывающий, как имитировать полиморфизм с помощью методов расширения.

void Main()
{
    var elements = new Base[]{
        new Base(){  Name = "Base instance"},
        new D1(){    Name = "D1 instance"},
        new D2(){    Name = "D2 instance"},
        new D3(){    Name = "D3 instance"}

    };

    foreach(Base x in elements){
        x.Process();
    }
}

public class Base{
    public string Name;
}
public class D1 : Base {}
public class D2 : Base {}
public class D3 : Base {}


public static class Exts{

    public static void Process(this Base obj){
        if(obj.GetType() == typeof(Base)) Process<Base>(obj); //prevent infinite recursion for Base instances
        else Process((dynamic) obj);
    }

    private static void Process<T>(this T obj) where T: Base
    {
        Console.WriteLine("Base/Default: {0}", obj.Name);
    }

    public static void Process(this D1 obj){
        Console.WriteLine("D1: {0}", obj.Name);
    }

    public static void Process(this D2 obj){
        Console.WriteLine("D2: {0}", obj.Name);
    }
}

Выходы:

    Base/Default: Base instance
    D1: D1 instance
    D2: D2 instance
    Base/Default: D3 instance

Ответ 5

Если вы не можете использовать ключевое слово "dynamic" (более старая версия .NET), вы можете использовать отражение для достижения того же.

Вместо:

Base base1 = new Child1();
Extensions.DoSomething(base1 as dynamic);

вы можете написать:

Base base1 = new Child1();

MethodInfo method = typeof(Extensions).GetMethod("DoSomething", new System.Type[] { base1.GetType() });
if (method) {
    method.Invoke(new object[] { base1 });
}