Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Interface Implementation relationship is just "Can-Do" Relationship?

Today somebody told me that interface implementation in C# is just "Can-Do" relationship, not "Is-A" relationship. This conflicts with my long-time believing in LSP(Liskov Substitution Principle). I always think that all inheritance should means "Is-A" relationship.

So, If interface implementation is just a "Can-Do" relationship. What if there is a interface "IHuman" and "IEngineer", and one class "Programmer" inherits from "IHuman" & "IEngineer"? Surely, a "Programmer" Is A "IHuman" and A "IEngineer".

If it is just "Can-Do" relationship, does it mean we cannot expect the "Programmer" instance behavior may be different between when treated as a IHuman and treated as IEngineer?

like image 553
Morgan Cheng Avatar asked Nov 01 '08 08:11

Morgan Cheng


3 Answers

I tend to think of interfaces as a contract of behaviour. Interfaces such as IComparable and IEnumerable are classic examples.

In the example you gave, IHuman and IEngineer are not really behaviours.

like image 121
Mitch Wheat Avatar answered Oct 28 '22 08:10

Mitch Wheat


In my experience it doesn't really help that much to think of "is-a" and "can-do" relationships. You rapidly get into problems. It's an impedance mismatch between the real world and OO, basically. However much people actually talk about modeling the real world, you fundamentally need to understand what the relationships between types mean on the platform you're using.

Sometimes interfaces can be used as capabilities, and sometimes they can represent more of a normal "is-a" relationship. I wouldn't get too hung up about it - just make sure you understand what they can do and what they can't.

like image 23
Jon Skeet Avatar answered Oct 28 '22 07:10

Jon Skeet


Found this question rather late but I wanted to chime in.

Interfaces in C# have an is-a relationship, but not is-an-object. Rather, is-an-implementation.

In other words, for class Foo that implements IBar, the following test:

Foo myFoo = new Foo(); 
return myFoo is IBar;

literally returns true. You can also say,

IBar bar = myArrayList[3] as IBar;
Foo foo = bar as Foo;

Or, if a method requires an IBar, you can pass a Foo.

void DoSomething(IBar bar) {
}

static void Main() {
    Foo myFoo = new Foo();
    DoSomething(myFoo);
}

Obviously, an IBar has no implementation in itself, so the can-do relationship also applies.

interface IBar { void happy(); }
class Foo : IBar
{
    void happy()
    {
        Console.Write("OH MAN I AM SO HAPPY!");
    }
}
class Program
{
    static void Main()
    {
        IBar myBar = new Foo();
        myBar.happy();
    }
}

But, for that matter, the same is true of object inheritance; an object of class Foo that inherits class Bar has a can-do relationship as well as an is-a relationship, in the same way as the interface does. It's just that its implementation was pre-built for it.

The real question is, is-a-what, can-do-what? An inherited class object is-a-[parent instance] and can-do-[parent's behavior], whereas an implementation of an interface is-an-[interface implementation] and therefore can-do-[the interface behavior].

In most cases where programmatic is-a relationships are used such as those listed above, only the interface is evaluated, so both an inherited class and an implemented interface share the same is-a and can-do qualities.

HTH, Jon

like image 35
Jon Davis Avatar answered Oct 28 '22 07:10

Jon Davis