Полиморфизм через методы расширения?
У меня есть библиотека классов, которые содержат некоторые базовые классы и другие, полученные из них. В этой библиотеке классов я использую полиморфизм, чтобы делать то, что хочу. Теперь в потребляющем приложении я хочу изменить поведение некоторого кода на основе типа времени выполнения дочерних классов. Поэтому предположим следующее:
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 });
}