2013-12-20 37 views
10

我有一个类库,其中包含一些基类和从它们派生的其他类。在这个类库中,我利用多态性来做我想做的事情。现在在一个消费应用程序中,我想根据子类的运行时类型来更改某些代码的行为。因此假定:通过扩展方法的多态性?

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的类,并且相应的扩展方法可能来自其他外部程序集。

在此先感谢。

+5

你为什么不尝试一下先,然后用如有必要,什么地方出了错细节问? –

+0

好的会做到这一点。 – Kassem

+1

这不起作用;扩展方法是静态调度的。 – SLaks

回答

7

正如@SLaks已经说明你不能调用方法,扩展方法(即使有dynamic型)...你可以调用但是用dynamic

所以静态方法,虽然这将失败

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

这将工作

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

这实际上工作!非常聪明,我不得不说!所以底线是我可以用一个静态方法实现它,并按照你的建议调用它,但是它不能使用在实例上调用的扩展方法来完成,对吧? – Kassem

+0

@Kassem是的,这是我可以在这里工作的唯一方式 – qujck

+1

这真的让我感到惊讶。 AFAIK,扩展方法(不适用于动态)由编译器转换为相同的静态方法调用(可以工作)。在每种情况下都会看到IL会很有趣(但现在不能访问编译器)。任何人都可以解释为什么这可能是? – Baldrick

4

不,这是行不通的。

扩展方法是静态分派的,使用与重载解析相同的机制。

如果编译时类型为Base的变量,编译器将始终调用基本扩展方法,而不管运行时类型如何。

相反,您可以使基本扩展方法检查运行时类型并调用适当的其他扩展方法。

+1

我试图避免检查运行时类型,因为可以添加更多从“Base”派生的类,并且相应的扩展方法可能来自某个其他外部程序集。 – Kassem

1

我一直在寻找同样的事情,刚才。

你可以多一个方法添加到您的扩展类是这样的:

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); 
} 

[编辑]其实这不会在你的情况下工作,因为你没有实现对所有派生对象的支持(所以仍然可以获得无限递归)。我想你可以传递一些东西来检查递归,但给定的答案是最简单的。我会留在这里,因为它可能引发更多的想法。

1

下面是显示如何用扩展方法模拟多态性的最简单示例。

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 
0

如果您不能使用关键字“动态”(旧版本的.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 }); 
}