Reputation: 13898
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
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:
AccountingBase
inherits from. That's probably the best idea. Then make the type of the Parent
property that nongeneric type.AccountingBase
generic in both itself and its parent... but that ends up causing recursion issues, effectively...Upvotes: 6
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