继承 - 调用 child 方法而不是 parent

Inheritance - Invoke child methods instead of parent

很难解释为什么我会做我即将向您展示的事情,但他们有理由留在我这里。 (欢迎提出建议)。

我有一个 Functor,它在其输入上调用一个方法。

!请注意!仿函数实际上是一个扩展方法,因此必须有类型推断。

此外,我有一个带有 2 child 的抽象 class 和一个需要方法签名的接口。

示例代码如下所示:

public sealed class AbstractionTester
{
    internal static void Run()
    {
        // The functor here accepts A type but in my original code its just a generic type. 
        // I wanted to keep it simple for this example only 
        Func<A, bool> func = a =>
        {
            a.CallMe(); //Displays "Error"
            return true;
        };

        B obj = new B();
        func(obj);
    }
}

internal interface ICallMe<T> 
    where T : MyEntity
{
    T CallMe();
}

//Just a class which holds data I would like to store about every object I have, for example: CreateDate
internal abstract class MyEntity
{ }

internal abstract class A : MyEntity, ICallMe<A>
{
    //some other fields i would like to store..

    // This method here must never be invoked
    public A CallMe()
    {
        //throw new Exception();
        Console.WriteLine("Error");
        return this;
    }
}

internal class B : A, ICallMe<B>
{
    public new B CallMe()
    {
        Console.WriteLine("B");
        return this;
    }
}

internal class C : A, ICallMe<C>
{
    public new C CallMe()
    {
        Console.WriteLine("C");
        return this;
    }
}

每次我调用 运行() 时,结果是 Error 出现在屏幕上。 我该怎么做才能强制我拥有的这个仿函数不会执行 parent class.

中的方法

Functor 永远不会收到 A 的实例,因为 A 是抽象的(我的意思是纯 A,而不是 A 的 child)

附加信息:

这是一个解决方案,无需将 public A CallMe() 定义为 virtual。这样做的好处是 child 类 可以将他们的 CallMe() 定义为 new,这样他们就可以 return BC。但它要求您可以使 类 public 而不是 internal (否则您将得到 an error)。

使用动态调度调用实际的运行时类型而不是接口中声明的类型:

Func<A, bool> func = a => {
     var runtimeType = (dynamic)a;
     runtimeType.CallMe(); 
     return true;
 };

.net Fiddle

这有一个特定的语言功能;界面重新实现。

显式重新实现接口并使通用仿函数采用 ICallable<T>:

internal class B : A, ICallMe<B>
{
    B ICallable<B>.CallMe()
    {
        Console.WriteLine("B");
        return this;
    }
} 

internal class C : A, ICallMe<C>
{
    B ICallable<C>.CallMe()
    {
        Console.WriteLine("B");
        return this;
    }
}

你的函子应该是:

Func<T, bool> func = a => ... 

并且 T 应该(在方法或 class 级别)限制为 ICallable<T>

更新:如果函子真的是一个扩展代码,我不确定是什么问题:

 public static bool MyEnxtensionMethod<T>(T argument)
     where T: ICallable<T>
 {
      argument.CallMe();
      return true;
 }

为什么你需要在任何地方保留 A

确保永远不会调用 A 的 CallMe 方法的最佳方法是让它 不存在。

internal abstract class MyEntity
{ }

internal abstract class A : MyEntity
{ }

现在永远无法按您的要求调用。

现在使接口协变:

internal interface ICallMe<out T> 
    where T : MyEntity
{
    T CallMe();
}

然后把Func<A, bool>改成Func<ICallMe<A>, bool>

public sealed class AbstractionTester
{
    internal static void Run()
    {
        // The functor here accepts A type but in my original code its just a generic type. 
        // I wanted to keep it simple for this example only 
        Func<ICallMe<A>, bool> func = a =>
        {
            a.CallMe(); //Displays "B"
            return true;
        };

        B obj = new B();
        func(obj);
    }
}

这真是奇怪的实现。为什么不使用 访客模式

那么你可以这样做:

    static void Main(string[] args)
    {
        Element a = new A();
        Element b = new B();
        Element c = new C();
        ICallMe callMe = new CallMe();
        a.accept(callMe);
        b.accept(callMe);
        c.accept(callMe);
    }

下面的实现:

public interface ICallMe
{
    void Visit(A a);
    void Visit(B b);
    void Visit(C c);
}

public class CallMe : ICallMe
{
    public void Visit(A c)
    {
        Console.WriteLine("A");
    }

    public void Visit(B b)
    {
        Console.WriteLine("B");
    }

    public void Visit(C a)
    {
        Console.WriteLine("C");
    }
}

interface Element
{
    void accept(ICallMe visitor);
}

public class A : Element
{
    public void accept(ICallMe visitor)
    {
        visitor.Visit(this);
    }
}
public class B : Element
{
    public void accept(ICallMe visitor)
    {
        visitor.Visit(this);
    }
}
public class C : Element
{
    public void accept(ICallMe visitor)
    {
        visitor.Visit(this);
    }
}