Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通过扩展方法实现多态性?_C#_.net_Polymorphism_Extension Methods - Fatal编程技术网

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