接口重新实现的替代方案

Alternatives to interface reimplementation

我正在阅读 Joe Albahari 出色的“C# 9 in a Nutshell”中的以下摘录,并试图理解此处以粗体显示的内容。有谁能以我能更好理解的方式解释替代方法吗?出于某种原因,这对我来说似乎有些落后。

Alternatives to interface reimplementation Even with explicit member implementation, interface reimplementation is problematic for a couple of reasons:

The subclass has no way to call the base class method.

The base class author might not anticipate that a method be reimplemented and might not allow for the potential consequences.

Reimplementation can be a good last resort when subclassing hasn’t been anticipated. A better option, however, is to design a base class such that reimplementation will never be required. There are two ways to achieve this:

When implicitly implementing a member, mark it virtual if appropriate.

When explicitly implementing a member, use the following pattern if you anticipate that subclasses might need to override any logic:

public class TextBox : IUndoable
{
  void IUndoable.Undo()         => Undo();    // Calls method below
  protected virtual void Undo() => Console.WriteLine ("TextBox.Undo");
}

public class RichTextBox : TextBox
{
  protected override void Undo() => Console.WriteLine("RichTextBox.Undo");
}

If you don’t anticipate any subclassing, you can mark the class as sealed to preempt interface reimplementation.

据我了解,作者想说的是,与其隐藏基class的成员,不如覆盖它们。隐藏是指子 class 有一个具有相同签名的成员,但它在基 class 中没有标记为虚拟。 即如果 ParentClass 有方法 A();如果您希望它被 ChildClass 覆盖,请将 A() 设为虚方法。或者,如果您不希望 A() 被覆盖,请引入一个受保护的方法,该 A() 将使用该受保护的方法并使该受保护的方法成为虚拟的,以便子代在需要时可以覆盖。 希望这能回答您的问题。

所描述的问题是,当基 class 实现接口时,基 class 可能会发生某些行为。

文本框撤销的例子。假设基础文本框做了一些重要的(或者可能是实际的工作)用于撤销。 同样为了举例,假设作者在写baseclass的时候并没有想到继承classes(这里是richtextbox)

他本可以写成

public class TextBox : IUndoable
{
  void IUndoable.Undo() => ....... undo logic here
}

任何继承 class 的都不能直接调用 Undo (他们必须将其基数转换为 IUndoable ) 现在的问题是,当其他代码使用 IUndo 实现(例如菜单项)时,不会调用基础 class 中的重要撤消,除非继承 class 明确地执行此操作

还是一样:

public class TextBox : IUndoable
{
  public void Undo() => ...... undo logic here
}

继承(RichTextBox)class、可以在显式实现撤消时调用其基类,但不确定。 如果 RichTextBox 自己做 Undo 并隐藏基础 Undo,则没有调用基础 Undo 的直接冲动(尽管比第一个选项更重要)

最后,基础class作者想要的是,当外部代码调用IUndoable.Undo方法时,总是调用所需的代码。 (旁注:在一个抽象的 class 中,可以以不同的方式处理,但这是一个可以直接使用的 class,可能会或可能不会被继承)

将隐式实现虚拟化会有所帮助:

public class TextBox : IUndoable
{
  public virtual void Undo() => ...... undo logic here
}

一旦继承 class 重写该方法,默认代码段也会调用基 class,但前提是使用了该代码段,并且仍会调用 base.Undo , 将选择权留给继承人。

这给出了您包含的最后一个示例:

public class TextBox : IUndoable
{
  void IUndoable.Undo()         => Undo();    // Calls method below
  protected virtual void Undo() => Console.WriteLine ("TextBox.Undo");
}

这为继承 classes 提供了一个更安全的选项来实现他们自己的撤销,而不会丢失基础 class 的撤销(除非继承 class 明确实现 IUndoable 也一样,但我们不要去那里 :P )

在 richtextbox 示例中,撤消被覆盖,但基本撤消被调用。正是这种情况可能导致 'undo' 的基本 class 实现被跳过。 但是在这里,如果某些东西(例如菜单项)调用了 IUndoableUndo,则显式实现被调用,强制要求的基础 class 撤消功能变为 运行,而且继承 class (richtextbox)

的覆盖实现

Is anyone able to explain the alternative approach in a way that I can understand better?

它只是说,如果您尝试执行我的以下代码,它将无法编译。

那么如果你需要使用显式接口实现你该怎么办一个继承class需要改变[=22= 】 那行为?好吧,您使用问题中提到的 exact 代码。它允许您使用显式接口实现并且允许它被覆盖。

就是这样。这就是它所说的全部内容。

using System;
        
public interface IUndoable {
    void Undo();
}


public class TextBox : IUndoable
{
  void IUndoable.Undo()         => Console.WriteLine ("TextBox.Undo");
}

public class RichTextBox : TextBox
{
    void IUndoable.Undo()         => Console.WriteLine ("RichTextBox.Undo");

}

public class Program
{
    public static void Main()
    {
    }
}