7

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.

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

7 Answers 7

5

And what the CLR is supposed to do if someone calls ((IBaseAction)a1).CanSave()? There could be just one implementation for 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. E.g. in this particular case, you're better off with the Role class model (also known as the Role pattern).

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for pointing out my mistake. Since these interfaces are semantic aspects of my base action I'll just have to leave the option to explicitly redefine my interface methods where ever I need
5

You can't do what you describe. Just imagine what would happen if a client requests the IBaseAction interface. Which one should be returned?

It sounds to me like each action should be implemented by separate objects.

2 Comments

I cannot do the separate implementation, but you're right that I cannot do exactly what I described.
I don't see how you can do it without separate objects but I don't know your full problem
3

You're trying to implement diamond inheritance with 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:

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 IActs. 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.

1 Comment

+1, the first code is good to know, didn't think of it myself, however the second one is closer to what I want!
1

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().

Comments

0

It's not defined by IAction1 but by IBaseAction.

The solution is to not let complex action implement both (your current solution might be breaking SRP)

Comments

0

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).

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.

5 Comments

+1 for taking the time to write code. My real solution is similiar, just that I won't explicitly implement EVERY method, only those I want to differ from my base implementation (in this case IAction1, as IAction2,3,4,5.... has to be implemented the same way (the default))
@Avada: Probably because it's ugly to continue down the path of that design, considering he's got multiple members on multiple interfaces to explicitly implement. It's just hard to maintain multiple implementations like that.
@TDaver: Good thing you could simplify it like that. You'd only have to re-declare IBaseAction's members on IAction1 in that case, as the others would be implemented as IBaseAction's members.
Surely, but the wierdness in the design is as well outlined in your answer. Thus I see no reason for downvoting. Well well, order restored by TDaver :)
Sometimes it can be appropriate to have a derived interface shadow a member defined in a previous interface. An especially common (and annoying) case exists when a base interface defines a read-only property and a derived interface defines a read-write property.
0

This wont work. You are assuming that IAction1 inherits IBaseAction, but that is not the case. Interfaces cannot inherit other interfaces. You can check this out by using reflection to see the baseclass of IAction1. It will not be IActionBase.

What you are saying with your code is: when a class implements IAction1 then it is also required to implement IBaseAction. C# will then help you by assuming you are implementing IActionBase by just telling it you implement IAction1. Also when using a var of type IAction1 you can call members from IBaseAction becouse it knows it is implemented.

1 Comment

Perhaps this is subjective, but is it correct to say that "interfaces cannot inherit other interfaces"? If an IAction1 knows that IBaseAction members will be available, isn't that more or less the definition of inheritance? Interfaces can form (or be part of) an inheritance hierarchy in just the same way classes can. If you don't call it inheritance (for reasons based on reflection evidence or something else), what do you call it?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.