List.AddRange()
existe, mais IList.AddRange()
n'existe pas.
Cela me semble étrange. Quelle est la raison derrière cela?
Réponses
Trop de publicités?Parce qu'une interface doit être facile à implémenter et ne pas contenir "tout sauf la cuisine". Si vous ajoutez AddRange
vous devriez alors ajouter InsertRange
et RemoveRange
(pour la symétrie). Une meilleure question serait pourquoi il n'y a pas de méthodes d'extension pour l'interface IList
similaire à l'interface IEnumerable
. (les méthodes d'extension pour Sort
en place, BinarySearch
, ... seraient utiles)
Pour ceux qui veulent avoir des méthodes d'extension pour "AddRange", "Sort", ... sur IList,
Voici la méthode d'extension AddRange
:
public static void AddRange(this IList source, IEnumerable newList)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (newList == null)
{
throw new ArgumentNullException(nameof(newList));
}
if (source is List concreteList)
{
concreteList.AddRange(newList);
return;
}
foreach (var element in newList)
{
source.Add(element);
}
}
J'ai créé une petite bibliothèque qui fait cela. Je trouve cela plus pratique que de devoir refaire ses méthodes d'extension sur chaque projet.
Certaines méthodes sont plus lentes que List mais elles font le travail.
Voici le lien GitHub pour les intéresser:
En fait, personne, sauf les développeurs et architectes de la plateforme .Net
, ne peut répondre à cette question. Mais il y a quelques points qui pourraient être des raisons.
Dans cette réponse, je parlerai de classes non génériques, mais presque tous mes mots seront également corrects pour les classes génériques.
Avant que je passe à l'explication, j'aimerais mentionner pour ceux qui ne le savent pas, que List<>
et toutes les implémentations d'IList
ne sont pas censées être une Liste en termes de programmation et de structures de données communes, cela signifie généralement liste chaînée. Et dans la documentation Microsoft de IList, nous pouvons voir la définition :
Représente une collection d'objets qui peuvent être accédés individuellement par index.
Donc, généralement, en lisant cette définition, vous ne devriez pas avoir de question sur "Pourquoi AddRange
n'est pas présenté dans IList
", mais "Pourquoi Add
est présenté?". Et, en parlant de Add
, il n'est pas dans l'interface IList
, mais dans l'interface ICollection
. Et c'est une chose vraiment étrange. Pourquoi? Parce que presque toutes les collections dans le .Net Standard héritent de ICollection
. Et en raison de cela, il y a beaucoup d'endroits dans le code source .Net, où nous pouvons voir l'implémentation de Add
comme dans la classe Array (Oui, Array
implémente également IList
) :
int IList.Add(Object value)
{
throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
}
Il y a plus de choses que je pourrais dire sur les relations entre les interfaces de collections en C# (aussi sur IReadOnlyList, qui a été ajouté bien après IList
et ressemble à une chose que IList
était censé être). Mais je pense qu'il y a assez de contexte, et nous pouvons commencer à parler des raisons concrètes pour lesquelles IList
n'a pas de AddRange
, mais List<>
en a.
-
Toutes les implémentations d'
IList
ne sont pas censées avoir une méthodeAddRange
.Comme je l'ai mentionné ci-dessus, il y a un problème avec la méthode
Add
. Beaucoup de collections en C# l'ont en fait, mais lèvent uneNotSupportedException
lorsqu'elle est appelée. Et la même situation (voire pire) se produirait avec la méthodeAddRange
. Donc seulList<>
a besoin de cette méthode, mais toutes les autres implémentations d'IList
n'en ont pas besoin. De plus, ces développeurs qui décident de créer leur propre implémentation d'IList
devront implémenterAddRange
, ce qui ne semble pas être quelque chose de vraiment nécessaire pour une collection indexée simple (comme l'IList
est). -
AddRange
est fortement dépendant de l'implémentation deList
.En parlant de la classe
List<>
. Il n'y a pas de classe non générique appeléeList
. La variante non générique s'appelleArrayList
. EtArrayList
est une sorte de synonyme de Tableau Dynamique en termes de structures de données. Je ne sais pas pourquoi il a été décidé de renommerArrayList
enList
dans les collections génériques, mais je pense que cela augmente simplement la confusion sur ces classes en C#. Ainsi,List
est en fait un tableau dynamique. Et un tableau dynamique aurait de gros problèmes de performance si vous ajoutez un grand nombre d'éléments un par un. DoncAddRange
est une méthode auxiliaire et, dans un sens, nécessaire pour un tableau dynamique. Mais elle n'est pas du tout nécessaire pour une collection indexée, qui est ce que l'IList
est.
En conclusion, je tiens à dire que List
et IList
(tout comme ArrayList
et IList
) sont en fait des entités qui ont une signification différente, et vous ne devez pas les considérer comme interchangeables. Mais il y a certaines mauvaises décisions concernant les noms et les relations d'interface qui ont été prises et ont conduit à une incompréhension croissante de la relation entre List
et IList
.
Depuis C#7, nous disposons de la correspondance de modèles que nous pouvons facilement utiliser pour appeler la méthode List.AddRange()
, plus performante, et n'avons pas besoin d'utiliser la conversion as
.
public static void AddRange(this ICollection collection, IEnumerable items)
{
if (collection is null)
throw new ArgumentNullException(nameof(collection));
if (items is null)
throw new ArgumentNullException(nameof(items));
switch (collection)
{
case List list:
list.AddRange(items);
break;
default:
foreach (var item in items)
{
collection.Add(item);
}
break;
}
}