Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dealing with mirrored inheritance trees

Tags:

c#

oop

Quite often I find myself dealing with a pattern similar to this one

enter image description here

Two inheritance trees, where there is some kind of mirroring. Each of the subclasses in the left tree has a different subclass in the right tree as source

The MappingEnd class:

public class MappingEnd
{
    public NamedElement source { get; set; }
}

The question is, how to deal with that in the subclasses. Do I hide the parent source property using the new keyword?

public class AssociationMappingEnd:MappingEnd
{
    public new Association source { get; set; }
}

Or do I simply provide a second property casting the NamedElement to Association ?

public class AssociationMappingEnd:MappingEnd
{
    public Association associationSource
    {
        get
        {
            return (Association)this.source;
        }
        set
        {
            this.source = value;
        }
    }
}

Why would I choose one over the other. Or is there a better way to implement this type of pattern?

like image 396
Geert Bellekens Avatar asked Sep 20 '18 13:09

Geert Bellekens


1 Answers

In the first design, public new Association source can easily cause trouble. You can still access the hidden member public NamedElement source accidentally. which is not cool.

For example, look at this pseudo-code:

IEnumerable<MappingEnd> MyMixedCollection = all kinds of MappingEnd objects

((AssociationMappingEnd)MyMixedCollection.First()).source //is cool
MyMixedCollection.First().source //access the hidden member, not cool

The new keyword is not the best design practice because it doesn't hide the inherited member. What it does is basically hiding the warning message that comes without it.

I personally never consider this as a valid design.


The second design is much more reliable because these names never get mixed up

AssociationMappingEnd.associationSource is Association
AssociationMappingEnd.source            is NamedElement

MappingEnd.source                       is NamedElement

However, it can get confusing since these source and associationSource are actually the same. It works, but it can be better.


We can't use override on source and give it a new type, we could use public virtual NamedElement source, but then we would lost type-safety. To retain type-safety we can use Generics, as elgonzo suggested.

The left side needs to be generic, because it depends on the types of its right side classes, so we should start with the top class on the left side:

public class MappingEnd<T> where T : NamedElement
{
    public T source { get; set; }
}

Now you can have either or both of these classes:

public class AssociationMappingEnd<T> : MappingEnd<T> where T : Association
{
}

public class AssociationMappingEnd : MappingEnd<Association>
{
}

Now you got only one source with a safe type:

AssociationMappingEnd<Association>.source   is Association
AssociationMappingEnd.source                is Association

MappingEnd<NamedElement>.source             is NamedElement
MappingEnd<Association>.source              is Association
like image 77
Bizhan Avatar answered Oct 20 '22 00:10

Bizhan