【问题标题】:Polymorphism Through Extension Methods?通过扩展方法实现多态性?
【发布时间】:2013-12-20 15:50:32
【问题描述】:

我有一个类库,其中包含一些基类和其他派生自它们的类。在这个类库中,我利用多态性来做我想做的事。现在在一个消费应用程序中,我想根据子类的运行时类型更改一些代码的行为。所以假设如下:

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

更新:

这不起作用。它总是调用基类的扩展方法。有没有其他方法可以让我这样做并避免显式检查运行时类型?原因是可以添加更多从Base 派生的类,并且相应的扩展方法可能来自其他一些外部程序集。

提前致谢。

【问题讨论】:

  • 您为什么不先尝试一下,然后在必要时询问问题的详细信息?
  • 那不行;扩展方法是静态分派的。
  • 考虑使用访问者模式。
  • 好吧,我试了一下,@SLaks 是对的。它总是调用基类的扩展方法。

标签: c# .net polymorphism extension-methods


【解决方案1】:

正如@SLaks 已经声明的那样,您不能将该方法作为扩展方法调用(即使使用dynamic 类型)...但是您可以使用dynamic 类型调用静态方法

所以,虽然这会失败

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

这会起作用

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

【讨论】:

  • 这确实有效!不得不说非常聪明!所以底线是我可以使用静态方法并按照您的建议调用它,但不能使用在实例上调用的扩展方法来完成,对吧?
  • 这真让我吃惊。 AFAIK,扩展方法(不适用于动态)由编译器转换为相同的静态方法调用(有效)。在每种情况下查看 IL 会很有趣(但现在无法访问编译器)。谁能解释为什么会这样?
  • 这太棒了,值得更多的爱,谢谢
【解决方案2】:

不,这行不通。

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

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

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

【讨论】:

  • 我试图避免检查运行时类型,因为可以添加更多从 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);
}

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

【讨论】:

    【解决方案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】:

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

      【讨论】:

        猜你喜欢
        • 2014-04-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多