Quite often I find myself dealing with a pattern similar to this one
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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With