2010-10-28 7 views
3

je la classe suivanteC# Generics Héritage

public class AccountingBase<TItemType> where TItemType : AccountingItemBase 

Et dans mon AccountingItemBase j'ai la propriété suivante:

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

dans mon AccountingBase, je suis en train de faire la

suivante
item.Parent = this; 

Logiquement cela devrait fonctionner, car TItemType hérite de AccountingItemBase, mais à la place je reçois le message suivant g erreur:

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

Comment puis-je définir les propriétés de l'enfant propriété parent lui-même (dans la classe parente)

+0

question stupide, puis-je voir la déclaration (y compris les espaces de noms) pour 'TItemType'? – jcolebrand

+0

@drachenstern: question stupide, laquelle? L'OP ou le vôtre? : -> – herzmeister

+0

@drachenstern: 'TItemType' est un paramètre de type. Il n'a pas de déclaration séparée. –

Répondre

6

Non, votre intuition est incorrecte. Cela ne devrait pas fonctionner, car les classes génériques ne sont pas différentes dans .NET.

Le fait que TItemType hérite de AccountingItemBase ne signifie pas que AccountingBase<TItemType> hérite de AccountingBase<AccountingItemBase>. Supposons que AccountingBase<TItemType> ait un champ de type TItemType. Ensuite, si votre intuition était correcte, vous pouvez écrire:

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

Cela romprait clairement la sécurité de type, parce que quand on les regarde comme AccountingBase<SomeSubtype>, le champ est censé être de type SomeSubtype, mais vous avez mis une valeur de type OtherSubtype là-dedans!

Fondamentalement, la variance générique est un sujet complexe.

Je vous suggère de lire le long et détaillé blog post series d'Eric Lippert pour plus d'informations. Ou j'ai une vidéo de NDC 2010 que vous pouvez trouver utile. Fondamentalement dans. NET 4 il ya une variance générique, mais c'est limité.

Maintenant, à ce que vous pouvez faire dans votre situation:

  • Vous pouvez créer une classe de base qui AccountingBase non générique hérite de. C'est probablement la meilleure idée. Ensuite, faites le type de la propriété Parent de type non-générique.
  • Vous pourriez faire AccountingBase générique dans les deux lui-même et sa mère ... mais qui finit par causer des problèmes de récursion, efficace ...
+0

Avez-vous essayé d'enseigner dans une université? – AlexanderMP

+2

@Alexander: Non, mais j'aime voir mon écriture en général comme une sorte d'enseignement. –

+0

@Jon Skeet ~ Et bien monsieur. Pour ma part, je détesterais te voir partir. PS: http://chat.stackoverflow.com/transcript/message/55711#55711 – jcolebrand

1

Outre les options de Jon, vous pouvez:

  • Créez une interface IAccountingBase qui fournit uniquement l'accès limité requis par AccountingItemBase pour effectuer son travail (similaire à une classe de base non générique, mais plus détaillée.)

  • Restructuration votre code afin que AccountingItemBase n'a pas besoin d'une référence Parent pour faire son travail. Mon expérience a été que les dépendances circulaires (le propriétaire connaît les éléments qui connaissent le propriétaire) sont symptomatiques d'un design où les instances enfants prennent trop de responsabilités.Vous pouvez parfois contourner ce problème en déplaçant la fonctionnalité vers le parent ou en la déplaçant vers des classes de niveau supérieur qui effectuent des opérations complexes sur plusieurs éléments dans le contexte d'un seul parent, ce qui élimine le besoin d'avoir une référence parent. Par exemple, si vos éléments exposent des fonctions liées à la réconciliation du compte, vous pouvez avoir une classe AccountReconciler au lieu de placer les fonctions sur les éléments.

+0

J'ai besoin de la référence parent car il est utilisé pour stocker dans la base de données, NHibernate sait à quel compte AccountingItem il appartient lors de la sauvegarde/mise à jour. –