Multiple base classes in .NET

B

Ben Voigt [C++ MVP]

Mark Wilden said:
If anything, I think GoF would say "prefer aggregation over private
inheritance."

Well, I've always heard that _public_ inheritance must be restricted to
cases of is-a. But surely there are cases when protected inheritance is
appropriate but public is not. For instance, if you are exposing a readonly
collection to your users, it may be helpful to inherit a full featured
collection privately, but make only the get accessors visible. Sure, you
could use aggregation/forwarding, but here the object is-a readonly
collection and also is-a writable collection, depending on the user. That
to me just sounds like protected inheritance. Of course, you expose all the
mutation functionality to your creator/owner by returning a pair of pointers
from a factory method, one cast to the mutable base class and one not.

Sadly, none of this is possible in .NET.
 
F

Frank Hileman

Hello Zachary,

The theoretical problem I am familiar with is resolving the correct member
to invoke when a member with the same name is implemented by multiple base
classes. I have not seen other problems except optimization. If members with
same name are disallowed, what problems are you referring to? Specifically,
what are the problems with the Scala approach?

I chose the property grid example because it does not work with aggregation:

- each aggregated class requires the complexity of a new TypeConverter for
serialization and property grid issues

- within the property grid, the properties are no longer grouped by the
CategoryAttribute, but by the aggregate, as it must now use an expandable
TypeConverter

- select multiple same-typed objects in a designer, and try to modify
individual aggregate properties independently

- the right-click reset operation resets the entire aggregate, not the
individual properties within

- the end user cannot access the properties directly off the instance at run
time, instead of instance.X it becomes instance.aggregate.X

- for the aggregate, one has to decide if a read-only property is provided,
or a read-write property with value semantics for the aggregate class. A
read-only property works fine when the parent does not need to be notified
about modifications, but Visual Studio has problems serializing these. A
read-write property with value semantics for the aggregate class requires
operator==, etc.

The scala example illustrates some of the power of mixins over single
inheritance. Inheritance has advantages over aggregation, otherwise we have
no need for even single inheritance.

Regards,
Frank Hileman

check out VG.net: http://www.vgdotnet.com
Animated vector graphics system
Integrated Visual Studio graphics editor
 
F

Frank Hileman

Hello,

I agree with the quote "designers overuse inheritance as a reuse
technique." One could qualify that by saying "inexperienced designers."

We should not conclude that aggregation is preferable to inheritance in all
contexts. Such a conclusion favors a "principle" (favor composition) over
the motivation behind the principle (inheritance abuse).

Not all use of inheritance is abuse. Mixins are no more susceptible to abuse
that other code reuse techniques. Ask people who work in languages with good
mixin support if mixins cause more problems than aggregation.

The Gof book is not a bible. It represents a set of opinions at a particular
point in history. At that point in time, static class diagrams (uml) were
becoming popular. People often assumed they needed to create the class
diagram up front, before implementing any of the leaves in the class
hierarchy.

Regards,
Frank Hileman

check out VG.net: http://www.vgdotnet.com
Animated vector graphics system
Integrated Visual Studio graphics editor
 
R

Richard

[Please do not mail me a copy of your followup]

"Frank Hileman" <[email protected]> spake the secret code
I agree with the quote "designers overuse inheritance as a reuse
technique." One could qualify that by saying "inexperienced designers."

I don't know that this qualification is of much use since everyone
starts out as an inexperienced designer. The problem I have seen is
that people build up bad habits while inexperienced and then after
having used these bad habits for a long time, they call themselves
experienced, yet they are still a poor designer.
We should not conclude that aggregation is preferable to inheritance in all
contexts.

That's why its a *preference* and not an outright ban on inheritance.
The Gof book is not a bible. [...]

There's no reason to get insulting by bringing religion into it.
 
L

Larry Smith

Well, I've always heard that _public_ inheritance must be restricted to
cases of is-a. But surely there are cases when protected inheritance is
appropriate but public is not. For instance, if you are exposing a
readonly collection to your users, it may be helpful to inherit a full
featured collection privately, but make only the get accessors visible.
Sure, you could use aggregation/forwarding, but here the object is-a
readonly collection and also is-a writable collection, depending on the
user. That to me just sounds like protected inheritance. Of course, you
expose all the mutation functionality to your creator/owner by returning a
pair of pointers from a factory method, one cast to the mutable base class
and one not.

Sadly, none of this is possible in .NET.

I don't think a language should restrict a user from doing this type of
thing but I personally find it extremely ugly. IMO (and it is a religious
issue) an interface is something that the "public" deals with and they
shoulsn't see anything else. If I use a vending machine to "download" a soft
drink, I want to see the interface only - coin slot, selection buttons,
change drop, etc. I don't want to see a second interface jutting out that
has meaning to the manufacuter only (even if I can't use it). It's unsightly
and confusing. The whole thing should be hidden which is not the case with
protected or private inheritance. Your own collection example is very ugly
in practice for instance (IMHO). This is because inheritance of this type
isn't normally done to extend a base class' behaviour by adding extra
functionality or even overriding existing behaviour. It's normally done to
support a new and specifc implementation meaning your new class requires its
own independent interface. A generic collection class for instance (say a
"vector" that actually supported derivation) will have many different
functions which become off-limits the moment I create a specific collection
of type T. My new collection is designed to handle T only which usually
involves very specific behaviour. It's not a "vector" from the user's
standpoint and shouldn't be seen that way. It's a collection of T and that's
how you want your users to see it (not as a "vector" even if it's
protected/private - it still violates the "is a" relationship from their
perspective). I also may not want them invoking the base class' "erase"
function, "capacity" function, "reserve" function, etc. I can inherit from
"vector" using "protected" or "private" and only expose what I want but
users can still see it's a "vector" which is an implementation detail they
shouldn't know about (especially since its functionality is off-limits). If
I add my own "erase" function for instance (maybe even calling it "delete")
then they'll now see two "erase" functions even if only one is accessible.
The entire situation is completely unnatural and confusing notwithstanding
the inconvenenience of aggregation since it requires new wrapper functions
you get for free using inheritance (although even with inheritance you may
need to replace a non-virtual base class function which leads to hiding
issues and/or dual functions in the derived class - this can result in
confusion and difficuties understanding the class even for those maintaining
it). Nevertheless, I readily acknowledge that aggregation is a maintenance
problem since you can wind up with many wrappers which often do little more
than delegate to others (resulting in code bloat, maintenance problems,
etc.).
 
M

Mark Wilden

Ben, you mentioned public, private and protected inheritance. My
understanding is that there is no conceivable use for protected inheritance,
and, indeed, Stroustrup felt that was a mistake.

///ark
 
B

Ben Voigt [C++ MVP]

Larry Smith said:
I don't think a language should restrict a user from doing this type of
thing but I personally find it extremely ugly. IMO (and it is a religious
issue) an interface is something that the "public" deals with and they
shoulsn't see anything else. If I use a vending machine to "download" a
soft drink, I want to see the interface only - coin slot, selection
buttons, change drop, etc. I don't want to see a second interface jutting
out that has meaning to the manufacuter only (even if I can't use it).
It's unsightly and confusing. The whole thing should be hidden which is
not the case with protected or private inheritance. Your own collection
example is very ugly

You were completely right... up until here. Non-public inheritance is
hidden from the user. Only members can cast to a private/protected base
class.
in practice for instance (IMHO). This is because inheritance of this type
isn't normally done to extend a base class' behaviour by adding extra
functionality or even overriding existing behaviour. It's normally done to
support a new and specifc implementation meaning your new class requires
its own independent interface. A generic collection class for instance
(say a "vector" that actually supported derivation) will have many
different functions which become off-limits the moment I create a specific
collection of type T. My new collection is designed to handle T only which
usually involves very specific behaviour. It's not a "vector" from the
user's standpoint and shouldn't be seen that way. It's a collection of T
and that's how you want your users to see it (not as a "vector" even if
it's

That's how they would see it -- via the public interfaces it implements.
protected/private - it still violates the "is a" relationship from their
perspective). I also may not want them invoking the base class' "erase"
function, "capacity" function, "reserve" function, etc. I can inherit from
"vector" using "protected" or "private" and only expose what I want but
users can still see it's a "vector" which is an implementation detail they

How? Protected/private base classes are implementation details and as such
are hidden from the user.
shouldn't know about (especially since its functionality is off-limits).
If I add my own "erase" function for instance (maybe even calling it
"delete") then they'll now see two "erase" functions even if only one is
accessible.

Functions in derived classes with the same name but different signatures
hide base members, they do not overload. This is why.
The entire situation is completely unnatural and confusing notwithstanding
the inconvenenience of aggregation since it requires new wrapper functions
you get for free using inheritance (although even with inheritance you may
need to replace a non-virtual base class function which leads to hiding
issues and/or dual functions in the derived class - this can result in
confusion and difficuties understanding the class even for those
maintaining


The using statement is a lot easier to maintain than a whole barrage of
trivial forwarders.
 
B

Ben Voigt [C++ MVP]

Mark Wilden said:
Ben, you mentioned public, private and protected inheritance. My
understanding is that there is no conceivable use for protected
inheritance, and, indeed, Stroustrup felt that was a mistake.

To specify the base class of a template class, where the exact class is
chosen by a descendant.

template <typename T>
class S : protected T
{
};

class R : S<std::vector<Q>>
{
};

Naturally the class choosing the base class should have access to it, but
with private inheritance, R couldn't use std::vector<Q>.
 
F

Frank Hileman

Hi Richard,

Sorry, I meant bible in the slang sense. GoF should not be considered a
source of absolute wisdom.

Frank

Richard said:
The Gof book is not a bible. [...]

There's no reason to get insulting by bringing religion into it.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top