简体   繁体   English

多个显式接口实现

[英]Multiple explicit interface implementation

I have the following base interface 我有以下基本界面

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

and two inheriting interface say 和两个继承接口说

public interface IAction1 : IBaseAction{}

and

public interface IAction2 : IBaseAction{}

My problem is, I have a class which implements both, and I want to implement CanAct DIFFERENTLY. 我的问题是,我有一个实现这两者的类,我想实现不同的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();

Is there a reasonably clean way to do this? 有一个相当干净的方式来做到这一点?
(Also, my interfaces have semantic meaning and at least three more functions, so it is out of the question to throw out the whole hierarchy, but I'd be willing to copy bool CanAct to every inheriting interface if that is the only solution (there are 4-6 of them)) (另外,我的界面具有语义和至少三个功能,因此抛弃整个层次结构是不可能的,但是我愿意将bool CanAct复制到每个继承接口,如果这是唯一的解决方案(其中有4-6个))

And what the CLR is supposed to do if someone calls ((IBaseAction)a1).CanSave() ? 如果有人调用((IBaseAction)a1).CanSave() ,CLR应该做什么? There could be just one implementation for IBaseAction.CanSave() . IBaseAction.CanSave()可能只有一个实现。 So I think you can't do this conceptually. 所以我认为你不能在概念上做到这一点。

This is a fundamental problem of multiple inheritance called the diamond problem . 这是多重继承的一个基本问题,称为钻石问题 The bottom line is: if you hit it, your type hierarchy design is definitely wrong. 底线是:如果你点击它,你的类型层次结构设计肯定是错误的。 Eg in this particular case, you're better off with the Role class model (also known as the Role pattern). 例如,在这种特殊情况下,您最好使用Role类模型 (也称为Role模式)。

You can't do what you describe. 你不能做你描述的。 Just imagine what would happen if a client requests the IBaseAction interface. 试想一下,如果客户端请求IBaseAction接口会发生什么。 Which one should be returned? 应归还哪一个?

It sounds to me like each action should be implemented by separate objects. 听起来我觉得每个动作应该由不同的对象实现。

You're trying to implement diamond inheritance with Interfaces. 您正在尝试使用Interfaces实现钻石继承。 The whole reason you're not allowed to implement multiple classes in the first place is to avoid diamond inheritance. 你不允许首先实现多个类的全部原因是避免钻石继承。

If you want to combine two interfaces together as a ComplexAction , you'd do something like the following: 如果要将两个接口组合在一起作为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();
    }
}

A ComplexAction is a composition of different IAct s. ComplexAction是不同IAct的组合。 If you're appending a number to an Interface name, chances are high that you're doing something wrong. 如果你在接口名称上附加一个数字,那么你做错了什么的可能性很高。

If instead, you want to define different behaviour based on the Interface, that Interface must have it's method defined on itself. 相反,如果您希望基于接口定义不同的行为,那么接口必须在其自身上定义它的方法。

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

To avoid the problems of diamond inheritance, you must give an implementation for ALL interfaces that define a particular method, so there is no ambiguity. 为了避免钻石继承的问题,您必须为定义特定方法的所有接口提供实现,因此不存在歧义。

I think you're trying to use inheritance for something it was not meant to do. 我认为你正试图将继承用于不应该做的事情。 If you have a complex action, it's composed of simpler actions, it's not multiple different actions at the same time. 如果你有一个复杂的动作,它更简单的动作组成 ,它不是同时有多个不同的动作。

Your ComplexAction should have properties Action1 and Action2 , of types IAction1 and IAction2 that have properly implemented CanSave() . ComplexAction应该有属性Action1Action2类型, IAction1IAction2已正确实施CanSave()

It's not defined by IAction1 but by IBaseAction . 它不是由IAction1定义的,而是由IBaseAction定义的。

The solution is to not let complex action implement both (your current solution might be breaking SRP) 解决方案是不要让复杂的操作同时实现(您当前的解决方案可能会破坏SRP)

You do need to re-declare the members of IBaseAction on the interfaces that need to defer, but then you also need to implement IBaseAction 's members implicitly or explicitly if there are no implicit implementations of the members (to make sure all interfaces are satisfied). 您需要在需要延迟的接口上重新声明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();
    }
}

It may be redundant to point out that this design is strange to have overlapping-named members that force you into jumping through hoops like this, but I figured I'd note it anyway. 可能有点多余的是指出,这种设计很难让重叠命名的成员强迫你像这样跳过箍,但我想我还是会注意到它。

You did say you can't toss your current hierarchy, but it might be possible to refactor it to avoid these shenanigans. 你确实说过你不能抛弃当前的层次结构,但有可能重构它以避免这些恶作剧。

This wont work. 这不行。 You are assuming that IAction1 inherits IBaseAction, but that is not the case. 您假设IAction1继承了IBaseAction,但事实并非如此。 Interfaces cannot inherit other interfaces. 接口不能继承其他接口。 You can check this out by using reflection to see the baseclass of IAction1. 您可以使用反射来查看IAction1的基类。 It will not be IActionBase. 它不会是IActionBase。

What you are saying with your code is: when a class implements IAction1 then it is also required to implement IBaseAction. 你对你的代码所说的是:当一个类实现IAction1时,它也需要实现IBaseAction。 C# will then help you by assuming you are implementing IActionBase by just telling it you implement IAction1. C#将通过假设您通过告诉它实现IAction1来实现IActionBase来帮助您。 Also when using a var of type IAction1 you can call members from IBaseAction becouse it knows it is implemented. 此外,当使用IAction1类型的变量时,您可以从IBaseAction调用成员,因为它知道它已实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM