3

I'm running into a situation where I have two levels of polymorphism, one within the other, in a parent/child hierarchy.

I think this is best explained by a simple example:

    class Group
    {
        public IList<Person> People { get; set; }
    }

    class SpecialGroup : Group
    {
        public IList<SpecialPerson> People { get; set; }
    }

    class Person {}

    class SpecialPerson : Person {}

So a Group has a list of Person objects, while a specialized Group (SpecialGroup) has a specialized list of Person (SpecialPerson) objects.

This compiles, but I get a warning saying I should use the "new" keyword on SpecialGroup.People, because it hides the Group.People property.

I understand what that means, but perhaps I don't fully comprehend how you would model something like this correctly in C# to begin with. Any thoughts on how to model this in a better way?

Also, any idea how this would play with NHibernate? I'm not sure using the "new" keyword will cut it, since the Group.People property would already be mapped with a different type. Is there a better way to model this, in a way that is compatible with NH?


Okay, I thought of a way to model this using generics:

    abstract class AbstractGroup<PersonType>
        where PersonType : Person
    {
        public IList<PersonType> People { get; set; }
    }

    class Group : AbstractGroup<Person>
    {}

    class SpecialGroup : AbstractGroup<SpecialPerson>
    {}

    class Person {}

    class SpecialPerson : Person {}

I think this does what I want? Now Person can share traits with SpecialPerson, but only a SpecialPerson can be added to a SpecialGroup. The AbstractGroup base-type can model the traits common to any Group.

Now the question is, will NH blow up if I try to map this?


For the record, it appears some people have successfully used query-over with generic classes - for HQL queries (my use-case) this is unsupported.

Another approach that occurred to me, is to simply hide the parent's implementation of a property, e.g. using the "new" keyword for an overriding property in a subclass - the following thread discusses why that won't work either:

NHibernate: Table Per Subclass Mapping and the New Keyword

Conclusion thus far: run-time checking and exceptions, as proposed by @empi below.

Community
  • 1
  • 1
mindplay.dk
  • 7,085
  • 3
  • 44
  • 54
  • Why does SpecialGroup need to inherit Group? – William Dwyer Feb 24 '12 at 19:28
  • Well, this was a very thin example - but SpecialGroup has a bunch of things in common with Group, so it does need to inherit. The example only shows what I thought was essential to the problem as such. – mindplay.dk Feb 24 '12 at 19:29

2 Answers2

2

If you want to enforce the rule that SpecialGroup contains only SpecialPerson then you may do it programatically without shadowing People property. I guess that you have some method AddPerson. You may override it in SpecialGroup and throw exception if someone tries to add entry that's not SpecialPerson. If you want get the list of SpecialPerson then you may add method IEnumerable<SpecialPerson> GetSpecialPeople() and cast Person to SpecialPerson (it will always be a valid cast since you're checking type in AddPerson method). By doing it you don't have to shadow People property and NHibernate will work just fine.

empi
  • 15,755
  • 8
  • 62
  • 78
  • Yeah, that'll work - every problem can be solved with more code. I was hoping to find a solution that takes advantage of strong typing and compile-time safety, rather than run-time checks... – mindplay.dk Feb 24 '12 at 19:40
  • How would you solve it without NHibernate? You're saying that Group contains collection of Person and then you're trying to say that SpecialGroup doesn't. You either have to check it (or hack it ;)) in your code or create some abstract group that doesn't have People in it. Of course you may try some solution with generics but I've never tried it with NHibernate. – empi Feb 24 '12 at 20:00
  • As I said _Of course you may try some solution with generics but I've never tried it with NHibernate_. http://ayende.com/blog/2951/nhibernate-and-generic-entities bottom line is _it is possible, but don't do it_ – empi Feb 24 '12 at 20:27
2

That case is also called "parallel" or "dual" inheritance.

Usually there is a specific class in one hierarchy, that relates to the other.

Altought, not required, its better to have a "generic" or "abstract" class of each one, that must be overriden, but, already has the concept of dependency.

This case its very used in Graphic Interface Controls, but, may apply to other scenarios.

In this particular case, Its usually better to only display the first level:

............................................................
....+----------------+................+----------------+....
....|  <<Abstract>>  |..<<contains>>..|  <<Abstract>>  |....
....| AbstractGroup  +<>--------------+ AbstractPerson |....
....|                |................|                |....
....+----------------+................+----------------+....
............................................................

Altought, you can model several levels, this is not recomended:

............................................................
....+----------------+................+----------------+....
....|  <<Abstract>>  |..<<contains>>..|  <<Abstract>>  |....
....| AbstractGroup  +<>--------------+ AbstractPerson |....
....|                |................|                |....
....+-------+--------+................+--------+-------+....
............|..................................|............
............|..................................|............
............^..................................^............
............|..................................|............
............|..................................|............
....+-------+--------+................+--------+-------+....
....|  <<Concrete>>  |..<<contains>>..|  <<Concrete>>  |....
....|      Group     +<>--------------+     Person     |....
....|                |................|                |....
....+----------------+................+----------------+....
............|..................................|............
............|..................................|............
............^..................................^............
............|..................................|............
............|..................................|............
....+-------+--------+................+--------+-------+....
....|  <<Concrete>>  |..<<contains>>..|  <<Concrete>>  |....
....|   SchoolGroup  +<>--------------+     Student    |....
....|                |................|                |....
....+----------------+................+----------------+....
............................................................

You mention NHibernate, does your classes represent data to be stored ?

Some similar questions:

Avoiding parallel inheritance hierarchies

Parallel Inheritance Hierarchy Refactoring

How to avoid parallel inheritance hierarchies among GUI controls and domain objects

Parallel Inheritance between Interface Classes and Implementation Classes in C++

Cheers.

Community
  • 1
  • 1
umlcat
  • 4,091
  • 3
  • 19
  • 29
  • I appreciate the effort! But yes, this is part of a persistent model - the child objects are not simply functions wrapped in objects, they are different entities with additional properties and functionality. Furthermore, I've discovered there is no support for generic queries in HQL. So I'm afraid @empi 's suggestion is the only one that will work in my case... – mindplay.dk Feb 27 '12 at 14:16
  • @mindplay.dk : I have work with database-entities & parallel hierarchies, but, never togheter. I also have tools that generate the entities, that have other missing features. I may eventually make my own entity generator, that supports the missing features ;-) – umlcat Feb 28 '12 at 17:59
  • what tool are you using for entity generation? I'm using one that does not work well at all... – mindplay.dk Feb 28 '12 at 22:53
  • @mindplay.dk : I use a custom tool I was provided at one of my jobs. Other job also had a tool of its own. I have also use codesmith, but, its paidware. I actually looking for an open source one, and already started my own, but, not finished. – umlcat Mar 01 '12 at 23:22