views:

181

answers:

2

I've stumbled upon this "feature" of C# - the base class that implements interface methods does not have to derive from it.

Example:

public interface IContract
{
    void Func();
}

// Note that Base does **not** derive from IContract
public abstract class Base
{
    public void Func()
    {
        Console.WriteLine("Base.Func");
    }
}

// Note that Derived does *not* provide implementation for IContract
public class Derived : Base, IContract
{
}

What happens is that Derived magically picks-up a public method Base.Func and decides that it will implement IContract.Func.

What is the reason behind this magic?

IMHO: this "quasi-implementation" feature is very-unintuitive and make code-inspection much harder. What do you think?

+10  A: 

Why do you think that this is strange and unnatural? Every public member of base class is also a public member of derived class. So there is no contradiction here. Anyhow you can implement interface explicitely if you like.

Voice
IMHO this kind of feature adds the unnecessary complexity to the language.The simple and natural way would be to derive the Base from IContract to explicitly state that it implements it.Humans are simple beings - we're not capable to instantly parse all the code and cross-reference the inheritence graph to understand that IContract.Func and Base.Func are related (renaming IContract.Func one will have to *manually* rename Base.Func). It becomes even more complicated when all three classes reside in different projects/solutions.
etarassov
In short - this feature *is evil* because the *code readability suffers greatly*. A programmer (possibly) has to go through every base class to find an interface implementation, even through the base classes that are completely unrelated to the interface.
etarassov
ok I'll try to explain: the situation you trying to describe is rare and unnatural. Especially this one - "It becomes even more complicated when all three classes reside in different projects/solutions.". Once again you can implement interface explicitely so you will avoid any collizion with base class methods.
Voice
@etarassov: Suppose you didn't implement Base but you did implement Derived. Then you cannot make it implement the interface you want. Suppose you want to implement Derived and OtherDerived, and OtherDerived is desired to NOT implement the contract. In both those cases you cannot add the interface to Base. Therefore some mechanism must be proposed which does not require the interface to be added to Base.
Eric Lippert
@Eric Lippert: good point. But still I tend to think that in these two (corner?) cases a composition should be preferred over inheritance, because of the complexity of the desired relation between Base, Derived and eventually OtherDerived. And many people advise to *prefer Composition over Inheritance* (in general).
etarassov
+5  A: 

The reason is that your comment is simply incorrect:

// Note that Derived does not provide implementation for IContract

Sure it does. Follow the logic through.

  • Derived is required to provide a public member corresponding to each member of IContract.
  • All inheritable members of a base class are also members of a derived class; that's the definition of inheritance.
  • Therefore Derived provides an implementation for IContract; its inherited member is a member that fulfills the requirement
  • Therefore, no error.

this feature is very-unintuitive and make code-inspection much harder. What do you think?

I think you shouldn't use the feature if you don't like it. If you find it confusing and weird to read code that uses this feature then encourage your coworkers who use this feature to stop doing so.

How is this feature different from any other feature where a method from a base class is used from a derived class? There are a number of different ways in which a method from a base class may be used or mentioned in a derived class -- method calls, overrides, method group conversions, and so on.

Furthermore, this is relatively speaking a simple, straightforward case. If you really want to complain about confusing interface semantics in C#, I'd spend my time complaining about interface reimplementation semantics. That's the one that really seems to bake people's noodles. I always have to look that thing up in the spec to make sure I'm getting the semantics right.

Eric Lippert
Thank you for the verbose answer! I *do* understand *how* it works, but IMHO the way it works is counter-intuitive for a number of reasons:(1) one reads the price-tag first and then pays, the same way one first *declares* and only *after* *implements* smth.(2) Derived is a IContract, does not implement it, but uses Base to implement IContract, which is not a IContract itself(3) By reading code for Base there is no way to decide if Base.Func is related to IContract.Func (the relation declare/implement).(4) Changing Base.Func will produce a compilation error, that won't even mention Base
etarassov
"I think you shouldn't use the feature if you don't like it." - 100% agree. Unfortunately other people could use it (or the existing codebase) which will make the code hard to read/understand. The truly great language guides developer to write good code (ideally would force it). C# (a very nice language) would only benefit if this feature is removed. But it would be really hard and painfull... Exactly as with closures inside a foreach using the loop-variable. Anyway thanks for the link to "interface reimplementation semantics"!
etarassov