views:

87

answers:

2

I have the following class

public class AccountingBase<TItemType> where TItemType : AccountingItemBase

And in my AccountingItemBase i have the following property:

public virtual AccountingBase<AccountingItemBase> Parent { get; set; }

in my AccountingBase, I am trying to do the following

item.Parent = this;

Logically this should work, as TItemType inherits from AccountingItemBase, but instead i get the following error:

> Error 1 Cannot implicitly convert type
> 'TGS.MySQL.DataBaseObjects.AccountingBase<TItemType>'
> to
> 'TGS.MySQL.DataBaseObjects.AccountingBase<TGS.MySQL.DataBaseObjects.AccountingItemBase>'

How can i set the child properties parent property to itself (inside the parent class)

+4  A: 

No, your intuition is incorrect. It shouldn't work, because generic classes aren't variant in .NET.

Just because TItemType inherits from AccountingItemBase doesn't mean that AccountingBase<TItemType> inherits from AccountingBase<AccountingItemBase>. Suppose AccountingBase<TItemType> had a field of type TItemType. Then if your intuition were correct, you could write:

AccountingBase<SomeSubtype> x = new AccountingBase<SomeSubtype>();
AccountingBase<AccountingItemBase> y = x;
y.SomeField = new OtherSubtype();

That would clearly break type safety, because when looked at as an AccountingBase<SomeSubtype>, the field is meant to be of type SomeSubtype, but you've put a value of type OtherSubtype in there!

Basically, generic variance is a complex topic.

I suggest you read Eric Lippert's long and detailed blog post series for more information. Or I have a video from NDC 2010 which you may find useful. Basically in .NET 4 there's some generic variance, but it's limited.

Now, as to what you can do in your situation:

  • You could create a nongeneric base class which AccountingBase inherits from. That's probably the best idea. Then make the type of the Parent property that nongeneric type.
  • You could make AccountingBase generic in both itself and its parent... but that ends up causing recursion issues, effectively...
Jon Skeet
Have you tried teaching at an university?
Alexander
@Alexander: No, but I like to view my writing in general as teaching of a sort.
Jon Skeet
@Jon Skeet ~ And well you do sir. I for one would hate to see you go. PS: http://chat.stackoverflow.com/transcript/message/55711#55711
drachenstern
Thanks for the great answer skeet, i originally designed my AccoutningBaseItem generic type to overcome this issue(so that the AccountingBaseItem knows what subclass it is), but that started annoying me that i have to pass an extra type for no reason around and it didn't feel like it was properly designed. I will do what you suggested in your first point, making the accountingbase inherit from non generic type. Thanks for the links, i will read them first.
LnDCobra
@Alexander, was that a compliment or not? :)
Benjol
@Benjol, actually I had felt a strong lack of such professionals in the University I spent 4 years in. In contrast, I got acquainted to a lot of local professionals, who don't teach to students what they know. I find it a bit cruel. I donno, just asking.
Alexander
I implemented the nongeneric base successfully, but now i have a runtime problem with nhibernate i am trying to debug with no luck so far. As it works when i step through the program but fails on loading the domain object when run without pauses, which makes it that much harder :( Thanks anyway
LnDCobra
+1  A: 

In addition to Jon's options, you could:

  • Create an interface IAccountingBase that provides only the limited access required by AccountingItemBase to do its work (similar to a non-generic base class, but further abstracted.)

  • Restructure your code so that AccountingItemBase doesn't need a Parent reference to do its work. My experience has been that circular dependencies (Owner knows about Items which know about Owner) are symptomatic of a design where the Child instances are taking on too many responsibilities. You can sometimes get around this by moving the functionality to the Parent or by moving it to higher-level classes that perform complex operations on multiple Items in the context of a single Parent, eliminating the need for each Item to have a Parent reference. For instance, if your Items expose functions related to Account reconciliation, you might have an AccountReconciler class rather than putting the functions on the Items.

Dan Bryant
I need the parent reference because it is used to store in the database, so that NHibernate knows which AccountingItem it belongs to when saving/udpating.
LnDCobra