【问题标题】:Inheritance - Invoke child methods instead of parent继承 - 调用子方法而不是父方法
【发布时间】:2017-07-20 22:50:31
【问题描述】:

很难解释我为什么要做我将要展示给你的事情,但他们有一个理由,所以留在我这里。 (欢迎提出建议)。

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

!请注意!函子实际上是一个扩展方法,所以必须有一个类型推断。

另外,我有一个带有 2 个孩子的抽象类和一个需要方法签名的接口。

示例代码如下所示:

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

每次我调用 Run() 时,结果都是 Error 出现在屏幕上。 我能做些什么来强制我拥有的这个函子不会执行父类中的方法。

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

附加信息:

  • 我必须在B 和C 类中明确写出CallMe 的返回类型。我无法将它们更改为输入 A。
  • 我需要在仿函数中保留 A 的类型(或类似的东西),因为我需要推断某些代码延续的类型。

【问题讨论】:

  • 为什么不在A的B类方法中override,而是使用new?
  • @Tigran 因为接口中定义了方法CallMe
  • 你将如何实现相同的通用接口两次?仅返回类型不同?
  • @JeroenvanLangen 我在哪里说过我实现了两次通用接口?如果您参考函子参数类型,它会推断类型,因为函子实际上是我代码中的扩展方法。如果你提到我写 ICallMe 的事实,这个例子被最小化到这个问题,在原始代码中我几乎在我拥有的每个类上都使用它。
  • 这里是:internal abstract class A : MyEntity, ICallMe&lt;A&gt; 和这里 internal class B : A, ICallMe&lt;B&gt; 所以:ICallMe&lt;A&gt; 和 ICallMe&lt;B&gt; 这就是问题所在。他们强制执行具有不同返回类型的 2 种方法。您不能定义仅在返回类型上有所不同的相同方法名。 (重载问题)如果将返回类型从T CallMe(); 更改为void CallMe(out T t),它将起作用。

标签: c# oop inheritance abstract-class abstract


【解决方案1】:

这是一个非常奇怪的实现。为什么不使用访客模式?

那么你可以这样做:

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

【讨论】:

  • 我实际上最后选择了动态调度,但我可能会将其更改为访问者模式(我在编写动态调度时也考虑过),它似乎是解决方案。
【解决方案2】:

这是一个无需将public A CallMe() 定义为virtual 的解决方案。这样做的好处是子类可以将它们的CallMe() 定义为new,这样它们就可以返回B 或C。但它要求你可以创建类public 而不是internal(否则你会得到an error)。

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

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

.net Fiddle

【讨论】:

  • 除非绝对需要,否则我宁愿不使用强制转换
  • 对不起,我想到的唯一其他方法是使用virtual,它不符合您的要求:(
【解决方案3】:

为此有一个特定的语言功能;接口重新实现。

显式地重新实现接口并使泛型函子采用ICallable&lt;T&gt;:

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 应该被限制(在方法或类级别)为ICallable&lt;T&gt;。

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

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

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

【讨论】:

  • 我不想在仿函数的参数和ICallable之间做一个约束。我需要识别那里的类型。如果您仔细阅读我的帖子,我提到函子实际上是一种扩展方法,因此我需要将类型 A 保留在其中。
  • @OriRefael 那么您如何考虑使其成为通用的(正如您在问题中指出的那样)并调用CallMe(),如果您不以某种方式限制泛型类型以便编译器知道调用是安全的?
  • @OriRefael 您的扩展方法的签名是什么?不贴相关代码很难给出答案。
【解决方案4】:

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

internal abstract class MyEntity
{ }

internal abstract class A : MyEntity
{ }

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

现在使接口协变:

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

然后将Func&lt;A, bool&gt;改为Func&lt;ICallMe&lt;A&gt;, bool&gt;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-11
    • 2016-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多