Michal Ciechan
Michal Ciechan

Reputation: 13898

C# Generics Inheritance

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)

Upvotes: 4

Views: 3085

Answers (2)

Jon Skeet
Jon Skeet

Reputation: 1503120

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...

Upvotes: 6

Dan Bryant
Dan Bryant

Reputation: 27515

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.

Upvotes: 2

Related Questions