2009-09-01 6 views
3

J'ai une interface générique IContrained qui est implémentée par la classe générique Contraint. Lorsque j'essaie de faire le code ci-dessous, j'obtiens une exception de distribution invalide.C# Casting Type générique

IConstrained<decimal> decimalLimit = new Constrained<decimal>(1); 
IConstrained<IComparable> comparableLimit = (IConstrained<IComparable>) decimalLimit; 

Pourquoi est-il pas possible de le faire si décimal implémente IComparable? Quelle serait la bonne façon de faire cela? Merci.

+0

http://stackoverflow.com/questions/tagged/covariance –

Répondre

0

En ce qui concerne les génériques, deux instances du même type générique avec des arguments de type différents n'ont pas de relation directe les unes avec les autres. En d'autres termes:

IConstrained<decimal> !== IConstrained<IComparable> 

Ceci est un problème de variance. Même si decimal est IComparable, IContrained < décimal > n'est pas IContraint <IComparable>. Je crois que ce type de conversion automatique sera possible sous C# 4.0 avec sa co/contravariance améliorée. Cependant, ce n'est pas possible avec C# 3.0 ou plus tôt.

+2

Ce ne sera pas le cas, car nous ne supportons que la variance sur les interfaces génériques construites avec des types * reference *. Decimal est un type * value *. –

+0

Donc, pas de variance sur les génériques construits avec des types de valeur. N'y aura-t-il aucune variance sur les types de valeur? Ou seulement dans le cas des génériques? – jrista

-1

Lire sur la contra-variance et la covariance. Réalisez que les génériques C# sont invariants et ce que vous demandez n'est pas ce que vous voulez.

4

Coulée IContraint décimale> à IContraint IComparable> s'appelle covariance. Vous ne pouvez pas le faire en C# 3. Cependant, il vient en C# 4.

Erik Lippert a une série d'articles de blog détaillant Contravariance and Covariance. Pour contourner le problème, vous devrez convertir la décimale en IComparable lorsque vous l'utiliserez.

+0

Merci, John. J'oublie toujours d'échapper aux moins de signes sur les types génériques. –

1

Il s'agit d'un trip-up courant avec C# (ainsi que d'autres langages avec Generics). En C#, vous pouvez uniquement transtyper en classes dans une hiérarchie de classes (superclasses, sous-classes). Mais IConstrained<IComparable> n'est ni une super-classe, ni une sous-classe de IConstrained<decimal>, même si la méthode décimale implémente IComparable. La raison pour laquelle C# n'autorise pas cela, c'est parce que l'autoriser signifierait que vous pouvez faire de très mauvaises choses.

Pour une explication détaillée des raisons pour lesquelles cela est, consultez this similar question

1

Pour une classe d'être coulable comme cela, il serait nécessaire de mettre en œuvre les deux interfaces IConstrained<decimal> and IConstrained<IComparable>

class A:IConstrained<decimal>,IConstrained<IComparable> 

Il ne se fait pas automatiquement, car .NET 2.0 n'implémente pas la covariance ou la contravariance. IConstrained<decimal> n'implémente pas IConstrained<IComparable>. Oui, c'est frustrant et contre-intuitif. Il y aura effectivement un certain soutien pour ce genre de scénario dans C# 4.0 sous une forme ou une autre de ce que je comprends. C'est ce qu'on appelle la covariance ou la contravariance. Editer: Je ne connais pas la classe Contrainte, mais vous pourriez être en mesure de construire un nouveau Contrained<IComparable> et de lui passer un nombre décimal. S'il avait un constructeur de la forme Constrained<T> (T copyFrom) alors vous pouvez déclarer un nouveau Constrained<IComparable> et lui transmettre la décimale. Un peu comme faire une copie.

Edit 2: À mi-chemin vers le bas cette page, recherchez « 2.0 », il est un exemple de la façon de contourner ce problème dans .NET 2.0: http://blog.t-l-k.com/dot-net/2009/c-sharp-4-covariance-and-contravariance

+0

".NET 2.0 n'implémente pas la covariance ou la contravariance" n'est pas vrai. La plate-forme elle-même (c'est-à-dire CLR) le supporte très bien, c'est juste qu'aucune classe de la bibliothèque de classes standard n'utilise les annotations de variance dans 2.0, et les langues ne les connaissent pas non plus. Mais vous pouvez coder les interfaces génériques covariantes et contravariantes dans MSIL dans 2.0, et les lancer directement à partir de MSIL, ou via 'object' dans C#. –

+0

J'aurais dû être plus précis et j'ai dit C# 2.0 – AaronLS

1

est ici une réponse à « comment faire il".

IList<decimal> decimalLimit = new List<decimal>(1); 
IEnumerable<IComparable> asComparable = decimalLimit.Cast<IComparable>(); 
IList<IComparable> comparableLimit = asComparable.ToList();