C# 通过扩展方法实现多态性?
我有一个类库,其中包含一些基类和从它们派生的其他类。在这个类库中,我利用多态性做我想让它做的事情。现在在一个消费应用程序中,我想根据子类的运行时类型更改一些代码的行为。因此,假设如下:C# 通过扩展方法实现多态性?,c#,.net,polymorphism,extension-methods,C#,.net,Polymorphism,Extension Methods,我有一个类库,其中包含一些基类和从它们派生的其他类。在这个类库中,我利用多态性做我想让它做的事情。现在在一个消费应用程序中,我想根据子类的运行时类型更改一些代码的行为。因此,假设如下: public class Base { } public class Child1 : Base { } public class Child2 : Base { } 现在在消费应用程序中,我希望执行以下操作(请注意,以下所有类都在消费应用程序中,不能在类库中引用): 更新: 这是行不通的。它总是调用基类的扩展
public class Base { }
public class Child1 : Base { }
public class Child2 : Base { }
现在在消费应用程序中,我希望执行以下操作(请注意,以下所有类都在消费应用程序中,不能在类库中引用):
更新:
这是行不通的。它总是调用基类的扩展方法。是否有其他方法允许我这样做并避免显式检查运行时类型?原因是可以添加更多从Base
派生的类,并且相应的扩展方法可以来自其他外部程序集
提前谢谢。不,那不行
扩展方法是静态调度的,使用与重载解析相同的机制
如果有编译时类型为Base
的变量,编译器将始终调用Base扩展方法,而不管运行时类型如何
相反,您可以让基本扩展方法检查运行时类型并调用相应的其他扩展方法。因为@SLaks已经声明,您不能将该方法作为扩展方法调用(即使使用
动态
类型)。。。但是,您可以使用dynamic
类型调用静态方法
因此,尽管这将失败
Base base1 = new Child1();
(base1 as dynamic).DoSomething();
这会奏效的
Base base1 = new Child1();
Extensions.DoSomething(base1 as dynamic);
我刚才也在找同样的东西 您可以向扩展类中再添加一个方法,如下所示:
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检查:
[编辑]实际上,这在您的情况下不起作用,因为您没有实现对所有派生对象的支持(因此仍然可能得到无限递归)。我想你可以传递一些东西来检查递归,但是给出的答案是最简单的。我将把它留在这里,因为它可能会激发更多的想法。下面是一个演示如何用扩展方法模拟多态性的最小示例
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);
}
}
如果不能使用关键字“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 });
}
你为什么不先尝试一下,然后在必要时详细询问出了什么问题?那是行不通的;扩展方法是静态调度的。考虑使用访问者模式。尝试使用OK,所以我尝试了,并且@ Salkes是正确的。它总是调用基类的扩展方法。我试图避免检查运行时类型,因为可以添加更多从
base
派生的类,并且相应的扩展方法可以来自其他外部程序集。这实际上起到了作用!我得说,我很聪明!所以底线是,我可以用一个静态方法实现它,并按照您的建议调用它,但是不能用在实例上调用的扩展方法来实现,对吗?这让我非常惊讶。首先,编译器将扩展方法(不适用于动态)转换为相同的静态方法调用(适用于动态)。在每种情况下看到IL都会很有趣(但现在还没有访问编译器的权限)。有人能解释为什么会这样吗?这太神奇了,值得更多的爱,谢谢
Base/Default: Base instance
D1: D1 instance
D2: D2 instance
Base/Default: D3 instance
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 });
}