Pourquoi IEnumerable a-t-il été fait covariant en C # 4?

Dans les versions antérieures de C #, IEnumerable était défini comme suit:

 public interface IEnumerable : IEnumerable 

Depuis le n ° 4, la définition est la suivante:

 public interface IEnumerable : IEnumerable 
  • Est-ce juste pour faire disparaître les moulages ennuyeux dans les expressions LINQ?
  • Est-ce que cela ne posera pas les mêmes problèmes qu’avec ssortingng[] <: object[] (variance de tableau brisé) en C #?
  • Comment l’ajout de la covariance a-t-il été effectué du sharepoint vue de la compatibilité? Le code antérieur fonctionnera-t-il toujours sur les versions ultérieures de .NET ou une recompilation est-elle nécessaire ici? Qu’en est-il de l’inverse?
  • Le code précédent utilisant cette interface était-il ssortingctement invariant dans tous les cas ou est-il possible que certains cas d’utilisation se comportent maintenant différemment?

Les réponses de Marc et de CodeInChaos sont plutôt bonnes, mais pour append quelques détails supplémentaires:

Tout d’abord, il semblerait que vous souhaitiez en savoir plus sur le processus de conception que nous avons suivi pour créer cette fonctionnalité. Si tel est le cas, je vous encourage à lire la longue série d’articles que j’ai écrits lors de la conception et de la mise en œuvre de la fonctionnalité. Commencez par le bas:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

Est-ce juste pour faire disparaître les moulages ennuyeux dans les expressions LINQ?

Non, ce n’est pas seulement pour éviter les expressions Cast , mais c’est l’un des facteurs de motivation qui nous a encouragés à utiliser cette fonctionnalité. Nous avons réalisé qu’il y aurait une légère augmentation du nombre de “pourquoi ne puis-je pas utiliser une séquence de girafes dans cette méthode qui prend une séquence d’animaux?” questions, car LINQ encourage l’utilisation de types de séquence. Nous savions que nous voulions d’abord append une covariance à IEnumerable .

Nous avons en fait envisagé de rendre IEnumerable covariant même en C # 3, mais nous avons décidé que ce serait étrange de le faire sans introduire la fonctionnalité complète à l’usage de quiconque.

Est-ce que cela ne posera pas les mêmes problèmes qu’avec ssortingng[] <: object[] (variance de tableau brisé) en C #?

Cela n'introduit pas directement ce problème, car le compilateur autorise uniquement la variance lorsqu'il est connu qu'il est typé. Cependant, cela préserve le problème de variance de tableau cassé. Avec covariance, IEnumerable est implicitement convertible en IEnumerable . Par conséquent, si vous avez une séquence de tableaux de chaînes, vous pouvez la traiter comme une séquence de tableaux d'objects, puis vous avez le même problème qu'auparavant. : vous pouvez essayer de placer une girafe dans ce tableau de chaînes et obtenir une exception à l'exécution.

Comment l'ajout de la covariance a-t-il été effectué du sharepoint vue de la compatibilité?

Soigneusement.

Le code antérieur fonctionnera-t-il toujours sur les versions ultérieures de .NET ou une recompilation est-elle nécessaire ici?

Un seul moyen de le savoir. Essayez-le et voyez ce qui échoue!

C'est souvent une mauvaise idée d'essayer de forcer le code compilé avec .NET X à s'exécuter avec .NET Y si X! = Y, quelles que soient les modifications apscopes au système de types.

Qu'en est-il de l'inverse?

Même réponse.

Est-il possible que certains cas d'utilisation se comportent différemment maintenant?

Absolument. Faire une covariante d'interface où il était invariant auparavant est techniquement un "changement radical", car cela peut provoquer la rupture du code. Par exemple:

 if (x is IEnumerable) ABC(); else if (x is IEnumerable) DEF(); 

Lorsque IE n'est pas covariant, ce code choisit ABC ou DEF ou aucun des deux. Quand il est covariant, il ne choisit plus DEF.

Ou:

 class B { public void M(IEnumerable turtles){} } class D : B { public void M(IEnumerable animals){} } 

Auparavant, si vous appeliez M sur une instance de D avec une séquence de tortues comme argument, la résolution de surcharge choisit BM car il s'agit de la seule méthode applicable. Si IE est covariant, la résolution de surcharge choisit maintenant DM parce que les deux méthodes sont applicables et qu'une méthode applicable sur une classe plus dérivée bat toujours une méthode applicable sur une classe moins dérivée, que la correspondance du type d'argument soit exacte ou non. .

Ou:

 class Weird : IEnumerable, IEnumerable { ... } class B { public void M(IEnumerable bananas) {} } class D : B { public void M(IEnumerable animals) {} public void M(IEnumerable fruits) {} } 

Si IE est invariant, un appel à dM(weird) résout en BM. Si IE devient soudainement covariant, les deux méthodes DM sont applicables, les deux sont meilleures que la méthode de la classe de base et aucune n'est meilleure que l'autre. La résolution de surcharge devient ambigu et nous signalons une erreur.

Lorsque nous avons décidé de procéder à ces changements radicaux, nous espérions que (1) les situations seraient rares et (2) lorsque de telles situations se produisent, c'est presque toujours parce que l'auteur de la classe tente de simuler la covariance dans une langue. ça ne l'a pas. En ajoutant directement la covariance, espérons-le, lorsque le code "se rompra" lors de la recompilation, l'auteur pourra simplement supprimer le mécanisme délirant qui tente de simuler une fonctionnalité existante.

En ordre:

Est-ce juste pour faire disparaître les moulages ennuyeux dans les expressions LINQ?

Cela fait que les choses se comportent comme les gens s’attendent généralement ; p

Est-ce que cela ne posera pas les mêmes problèmes qu’avec ssortingng[] <: object[] (variance de tableau brisé) en C #?

Non; puisqu'il n'expose aucun mécanisme Add ou similaire (et ne peut pas; out et in sont appliqués au compilateur)

Comment l'ajout de la covariance a-t-il été effectué du sharepoint vue de la compatibilité?

La CLI la prenait déjà en charge, mais C # (et certaines des méthodes BCL existantes) en était simplement consciente.

Le code antérieur fonctionnera-t-il toujours sur les versions ultérieures de .NET ou une recompilation est-elle nécessaire ici?

Cependant, il est entièrement compatible avec les versions antérieures: C # qui repose sur la variance C # 4.0 ne comstackra pas dans un compilateur C # 2.0, etc.

Qu'en est-il de l'inverse?

Ce n'est pas déraisonnable

Le code précédent utilisant cette interface était-il ssortingctement invariant dans tous les cas ou est-il possible que certains cas d'utilisation se comportent maintenant différemment?

Certains appels BCL ( IsAssignableFrom ) peuvent renvoyer différemment maintenant

Est-ce juste pour faire disparaître les moulages ennuyeux dans les expressions LINQ?

Pas seulement avec LINQ. C’est utile partout où vous avez un IEnumerable et le code attend un IEnumerable .

Est-ce que cela ne posera pas les mêmes problèmes qu’avec ssortingng[] <: object[] (variance de tableau brisé) en C #?

Non, car la covariance n’est autorisée que sur les interfaces qui renvoient des valeurs de ce type, mais ne les acceptent pas. Donc c’est sûr.

Comment l’ajout de la covariance a-t-il été effectué du sharepoint vue de la compatibilité? Le code antérieur fonctionnera-t-il toujours sur les versions ultérieures de .NET ou une recompilation est-elle nécessaire ici? Qu’en est-il de l’inverse?

Je pense que le code déjà compilé fonctionnera principalement tel quel. Certaines vérifications de type à l’exécution ( is , IsAssignableFrom , …) IsAssignableFrom true là où elles ont renvoyé false auparavant.

Le code précédent utilisant cette interface était-il ssortingctement invariant dans tous les cas ou est-il possible que certains cas d’utilisation se comportent maintenant différemment?

Vous ne savez pas ce que vous entendez par là


Les problèmes les plus importants sont liés à la résolution de la surcharge. Étant donné que des conversions implicites supplémentaires sont possibles, une surcharge différente peut être choisie.

 void DoSomething(IEnumerabe bla); void DoSomething(object blub); IEnumerable values = ...; DoSomething(values); 

Mais bien sûr, si ces surcharges se comportent différemment, l’API est déjà mal conçue.