多个显式接口实现

7

我有以下基本接口

public interface IBaseAction
{
   bool CanAct(...)
}

并且有两个继承接口,分别是
public interface IAction1 : IBaseAction{}

并且。
public interface IAction2 : IBaseAction{}

我的问题是,我有一个实现了两个接口的类,而我想要以不同的方式实现CanAct。

public class ComplexAction : IAction1, IAction2
{
   bool IAction1.CanAct(...){} //doesn't compile as CanAct is not a member of IAction1!!
}

ComplexAction c=new ComplexAction();
var a1 = (IAction1)c;
var a2 = (IAction2)c;
a1.CanSave(); //THESE TWO CALLS SHOULD BE IMPLEMENTED DIFFERENTLY
a2.CanSave();

有没有比较干净的方法来实现这个?
(此外,我的接口具有语义意义和至少三个以上的功能,因此不可能放弃整个层次结构,但如果这是唯一的解决方案,我愿意将bool CanAct复制到每个继承接口中(它们共有4-6个))

7个回答

5

如果有人调用((IBaseAction)a1).CanSave(),那么CLR应该做什么呢?可能只有一个IBaseAction.CanSave()的实现。所以我认为在概念上你无法这样做。

这是多重继承中的一个基本问题,称为菱形继承问题。底线是:如果你遇到了这个问题,你的类型层次结构设计肯定是错误的。 例如,在这种特殊情况下,你最好使用角色类模型(也称为角色模式)。


谢谢您指出我的错误。由于这些接口是我基本操作的语义方面,我只能留下显式重新定义接口方法的选项,无论我在哪里需要。 - TDaver

5
您所描述的操作是不可行的。想象一下,如果客户端请求 IBaseAction 接口会发生什么事情?应该返回哪一个?看起来每个操作都应该由单独的对象实现。

我无法进行单独的实现,但你说得对,我不能完全按照我描述的方式去做。 - TDaver
我不知道你的完整问题,但我认为你不能没有单独的对象来完成它。 - David Heffernan

3
你正在尝试使用接口实现钻石继承。首先不允许实现多个类的原因是为了避免钻石继承。如果你想将两个接口组合成一个名为“ComplexAction”的接口,可以按照以下方式进行:
interface IAct
{
    bool CanAct();
}

class Act1 : IAct
{
    public bool CanAct()
    {
        return true;
    }
}

class Act2 : IAct
{
    public bool CanAct()
    {
        return false;
    }
}

class ComplexAction : IAct
{
    private Act1 action1;
    private Act2 action2;

    public ComplexAction(Act1 action1, Act2 action2)
    {
        this.action1 = action1;
        this.action2 = action2;
    }

    public bool CanAct()
    {
        return action1.CanAct() && action2.CanAct();
    }
}

ComplexAction是由不同的IAct组成的复合动作。如果你在接口名称后面添加一个数字,那么很可能你正在做一些错误的事情。

相反,如果你想根据接口定义不同的行为,那么该接口必须在自身上定义其方法。

interface IAct1
{
    bool CanAct();
}

interface IAct2
{
    bool CanAct();
}

class SometimesAct1SometimesAct2 : IAct, IAct1, IAct2
{
    bool IAct1.CanAct()
    {
        return false;
    }

    bool IAct2.CanAct()
    {
        return true;
    }

    public bool CanAct()
    {
        Console.WriteLine("Called on IAct or SometimesAct1SometimesAct2");
        return false;
    }
}

为了避免钻石继承的问题,您必须为定义特定方法的所有接口提供实现,以消除歧义。

+1,第一段代码很好,我自己没有想到,但第二段更接近我的需求! - TDaver

1

我认为你试图使用继承来做一些它本不应该做的事情。如果你有一个复杂的操作,它是由更简单的操作组成的,而不是同时进行多个不同的操作。

你的ComplexAction应该有属性Action1Action2,类型分别为IAction1IAction2,并且已经正确实现了CanSave()方法。


0

您需要在需要推迟的接口上重新声明IBaseAction的成员,但是如果没有成员的隐式实现(以确保满足所有接口),则还需要显式或隐式地实现IBaseAction的成员。

interface IBase {
    void Act();
}

interface IAction1 : IBase {
    void Act();
}

interface IAction2 : IBase {
    void Act();
}

class MyClass : IAction1, IAction2 {
    public void Act() {
        Console.WriteLine( "satisfies IBase.Act()" );
    }
    void IAction1.Act() {
        Console.WriteLine( "IAction1.Act()" );
    }
    void IAction2.Act() {
        Console.WriteLine( "IAction2.Act()" );
    }

    static void Main( string[] args ) {
        MyClass cls = new MyClass();
        cls.Act();
        IAction1 a = cls;
        a.Act();
        IAction2 b = cls;
        b.Act();
        Console.ReadKey();
    }
}

可能有些多余,但我还是想指出这种设计很奇怪,因为它有重叠命名的成员,迫使你像这样跳过一些障碍。

你说过不能放弃当前的层次结构,但重新设计可能可以避免这些花招。


+1 鼓励你花时间编写代码。我的真正解决方案类似,只是我不会显式地实现每个方法,只有那些我想要与基本实现不同的方法(在这种情况下是 IAction1),因为 IAction2、3、4、5...必须以相同的方式实现(默认方式)。 - TDaver
@Avada:可能是因为继续沿着那种设计的道路走下去很丑陋,考虑到他有多个成员在多个接口上需要显式实现。这样维护多个实现就很困难。 - Joel B Fant
1
@TDaver:很好,你能这样简化它。在这种情况下,您只需要在IAction1上重新声明IBaseAction的成员,因为其他成员将作为IBaseAction的成员实现。 - Joel B Fant
1
当然可以,但是你的回答中也清楚地概述了设计的奇怪之处。因此我不明白为什么要投票反对。好吧,TDaver恢复了秩序 :) - Avada Kedavra
有时让派生接口覆盖先前接口中定义的成员可能是合适的。特别常见(且令人烦恼)的情况是,基础接口定义了一个只读属性,而派生接口定义了一个可读可写的属性。 - supercat

0

这不是由 IAction1 定义的,而是由 IBaseAction 定义的。

解决方案是不要让复杂的操作同时实现两者(您当前的解决方案可能会破坏 SRP)。


0

这样是行不通的。你假设 IAction1 继承自 IBaseAction,但事实并非如此。接口无法继承其他接口。你可以使用反射来查看 IAction1 的基类,它不会是 IActionBase。

你的代码所表达的意思是:当一个类实现了 IAction1 接口时,也必须实现 IBaseAction 接口。C# 将会帮助你通过仅仅告诉它你实现了 IAction1 接口而自动认为你也实现了 IActionBase 接口。此外,当使用类型为 IAction1 的 var 变量时,因为它知道已经实现了 IBaseAction 接口,所以你可以调用 IBaseAction 的成员方法。


也许这是主观的,但说“接口不能继承其他接口”是正确的吗?如果IAction1知道将可用IBaseAction成员,那不更或多或少地是继承的定义吗?接口可以以与类相同的方式形成(或成为)继承层次结构。如果您不称其为继承(基于反射证据或其他原因),那么您将如何称呼它? - Bob Sammers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接