44 votes

Pourquoi était IEnumerable <T> fait covariant en C # 4?

Dans les précédentes versions de C# IEnumerable a été défini comme ceci:

public interface IEnumerable<T> : IEnumerable

Depuis C# 4 la définition est la suivante:

public interface IEnumerable<out T> : IEnumerable
  • Est-il juste pour faire de l'ennuyeux jette dans les expressions LINQ s'en aller?
  • Ne pas ce présenter les mêmes problèmes comme avec string[] <: object[] (cassé la matrice de variance) en C#?
  • Comment a été l'ajout de la covariance fait à partir d'une compatibilité point de vue? Sera plus tôt code de toujours travailler sur les dernières versions de .NET ou est-recompilation nécessaire ici? Ce sujet dans l'autre sens?
  • A code précédent à l'aide de cette interface strictement invariant dans tous les cas, ou est-il possible que dans certains cas va se comporter de manière différente aujourd'hui?

53voto

Eric Lippert Points 300275

Marc et CodeInChaos réponses sont assez bon, mais juste pour ajouter un peu plus de détails:

Tout d'abord, il semble que vous êtes intéressé à en apprendre sur le processus de conception, nous sommes allés à travers pour rendre cette fonctionnalité. Si oui, alors je vous invite à lire ma longue série d'articles que j'ai écrit lors de la conception et de la mise en œuvre de la fonctionnalité. Commencer par le bas:

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+et+la contravariance/par défaut.aspx

Est-il juste pour faire de l'ennuyeux jette dans les expressions LINQ s'en aller?

Non, il n'est pas juste pour éviter Cast<T> expressions, mais cela a été l'un des facteurs de motivation qui nous a encouragés à faire de cette fonctionnalité. Nous avons réalisé qu'il y aurait une légère hausse dans le 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, parce que LINQ encourage l'utilisation des types de séquence. Nous savions que nous voulions ajouter la covariance pour IEnumerable<T> première.

Nous avons effectivement envisagé IEnumerable<T> covariante même en C# 3, mais a décidé qu'il serait étrange de le faire sans l'introduction de la fonction entière pour quiconque de l'utiliser.

Ne pas ce présenter les mêmes problèmes comme avec string[] <: object[] (cassé la matrice de variance) en C#?

Il n'est pas directement introduire ce problème parce que le compilateur ne permet à l'écart quand il est connu pour être typesafe. Cependant, il n' préserver le cassé de la matrice de variance problème. Avec la covariance, IEnumerable<string[]> est implicitement convertible IEnumerable<object[]>, donc si vous avez une séquence de tableaux de chaîne, vous pouvez traiter que comme une suite de tableaux d'objets, et puis vous avez le même problème qu'avant: vous pouvez essayer de mettre une Girafe dans le tableau de chaîne et obtenir une exception à l'exécution.

Comment a été l'ajout de la covariance fait à partir d'une compatibilité point de vue?

Soigneusement.

Sera plus tôt code de toujours travailler sur les dernières versions de .NET ou est-recompilation nécessaire ici?

Une seule façon de le savoir. Essayez et voyez ce ne!

C'est souvent une mauvaise idée d'essayer de forcer le code compilé contre .NET X pour exécuter sur .NET Y si X != Y, quelle que soit le type de système.

Ce sujet dans l'autre sens?

Même réponse.

Est-il possible que dans certains cas va se comporter de manière différente aujourd'hui?

Absolument. Faire une interface covariante où il était invariant avant est techniquement une "modification importante", car il peut causer code de travail à la pause. Par exemple:

if (x is IEnumerable<Animal>)
    ABC();
else if (x is IEnumerable<Turtle>)
    DEF();

Lors de l' IE<T> n'est pas covariante, ce code choisit soit ABC ou DEF ou aucun des deux. Quand il est covariant, il n'a jamais choisit DEF plus.

Ou:

class B     { public void M(IEnumerable<Turtle> turtles){} }
class D : B { public void M(IEnumerable<Animal> animals){} }

Avant, si vous l'avez appelé M sur une instance de D avec une séquence de tortues marines, le raisonnement, la résolution de surcharge choisit B. M parce que c'est la seule méthode applicable. Si IE est covariant, puis la résolution de surcharge choisit D. M parce que les deux méthodes sont applicables, et une méthode applicable sur une classe dérivée de bat toujours une méthode applicable sur une classe dérivée, indépendamment du fait que l'argument de type match-ci, est exact ou pas.

Ou:

class Weird : IEnumerable<Turtle>, IEnumerable<Banana> { ... }
class B 
{ 
    public void M(IEnumerable<Banana> bananas) {}
}
class D : B
{
    public void M(IEnumerable<Animal> animals) {}
    public void M(IEnumerable<Fruit> fruits) {}
}

Si IE est invariante alors un appel à l' d.M(weird) décide de B. M. Si IE devient tout à coup covariante puis les deux méthodes D. M sont applicables, les deux sont meilleurs que la méthode de la classe de base, et aucune n'est meilleure que l'autre, donc, de résolution de surcharge devient ambigu et nous signaler une erreur.

Lorsque nous avons décidé de faire ces modifications importantes, nous espérions que (1) les situations sont rares, et (2) lorsque de telles situations surviennent, presque toujours, c'est parce que l'auteur de la classe est de tenter de simuler la covariance dans une langue qui ne l'a pas. Par l'ajout de covariance directement, j'espère quand le code "casse" sur la recompilation, l'auteur peut tout simplement supprimer la folle engins en essayant de simuler une fonctionnalité qui existe maintenant.

28voto

Marc Gravell Points 482669

Dans l'ordre:

Est-il juste pour faire de l'ennuyeux jette dans les expressions LINQ s'en aller?

Cela rend les choses se comporter comme les gens en général s'attendre ;p

Ne pas ce présenter les mêmes problèmes comme avec string[] <: object[] (cassé la matrice de variance) en C#?

Non; car il n'est pas vulnérable Add mécanisme ou similaires (et ne le pourront pas. out et in sont appliquées au compilateur)

Comment a été l'ajout de la covariance fait à partir d'une compatibilité point de vue?

La CLI déjà pris en charge, ce qui ne fait en C# (et un peu de BCL méthodes) au courant de ça

Sera plus tôt code de toujours travailler sur les dernières versions de .NET ou est-recompilation nécessaire ici?

Il est tout à fait compatible, cependant: C# qui s'appuie sur le C# 4.0 variance de ne pas compiler en C# 2.0, etc compilateur

Ce sujet dans l'autre sens?

Qui n'est pas déraisonnable

A code précédent à l'aide de cette interface strictement invariant dans tous les cas, ou est-il possible que dans certains cas va se comporter de manière différente aujourd'hui?

Certains BCL appels (IsAssignableFrom) peut retourner différemment maintenant

9voto

CodesInChaos Points 60274
  • Est-il juste pour faire de l'ennuyeux jette dans les expressions LINQ s'en aller?

Non seulement lors de l'utilisation de LINQ. Il est utile partout où vous avez un IEnumerable<Derived> et le code s'attend à un IEnumerable<Base>.

  • Ne pas ce présenter les mêmes problèmes comme avec string[] <: object[] (cassé la matrice de variance) en C#?

Non, parce que la covariance est seulement allowd sur des interfaces, et les valeurs de retour de ce type, mais ne pas les accepter. Donc, c'est sûr.

  • Comment a été l'ajout de la covariance fait à partir d'une compatibilité point de vue? Sera plus tôt code de toujours travailler sur les dernières versions de .NET ou est-recompilation nécessaire ici? Ce sujet dans l'autre sens?

Je pense que déjà le code compilé principalement du travail. Certains d'exécution type de contrôle (is, IsAssignableFrom,...) retourne true, où ils ont retourné false plus tôt.

  • A code précédent à l'aide de cette interface strictement invariant dans tous les cas, ou est-il possible que dans certains cas va se comporter de manière différente aujourd'hui? Pas sûr de ce que vous voulez dire par là

Les plus gros problèmes sont liés à la résolution de surcharge. Depuis maintenant supplémentaire conversions implicites sont possibles une autre surcharge peut être choisi.

void DoSomething(IEnumerabe<Base> bla);
void DoSomething(object blub);

IEnumerable<Derived> values=...;
DoSomething(values);

Mais bien sûr, si ces fonctions diffèrent dans le comportement de l'API est déjà mal conçu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X