Pourquoi les tableaux multidimensionnels C # n'implémentent-ils pas IEnumerable < T > ;?
-
07-07-2019 - |
Question
Je viens de remarquer qu'un tableau multidimensionnel en C # n'implémente pas IEnumerable < T >
, alors qu'il implémente IEnumerable
. Pour les tableaux à une dimension, IEnumerable < T >
et IEnumerable
sont implémentés.
Pourquoi cette différence? Si un tableau multidimensionnel est IEnumerable
, la version générique devrait-elle également être implémentée? J'ai remarqué cela parce que j'ai essayé d'utiliser une méthode d'extension sur un tableau multidimensionnel, qui échoue à moins d'utiliser Cast
ou similaire; je peux donc clairement voir un argument en faveur de la mise en œuvre de tableaux multidimensionnels IEnumerable < T >
.
Pour clarifier ma question dans le code, je m'attendrais à ce que le code suivant imprime true
quatre fois, alors qu'il imprime réellement true
, false
, true
, true
:
int[] singleDimensionArray = new int[10];
int[,] multiDimensional = new int[10, 10];
Debug.WriteLine(singleDimensionArray is IEnumerable<int>);
Debug.WriteLine(multiDimensional is IEnumerable<int>);
Debug.WriteLine(singleDimensionArray is IEnumerable);
Debug.WriteLine(multiDimensional is IEnumerable);
La solution
Le CLR a deux types de tableaux différents: les vecteurs garantis unidimensionnels avec une limite inférieure de 0 et les tableaux plus généraux pouvant avoir des limites non nulles et un rang autre que 0.
À partir de la section 8.9.1 de la spécification CLI:
De plus, un vecteur créé avec type d'élément T, implémente le interface
System.Collections.Generic.IList < U >
(& # 167; 8.7), où U: = T.
Je dois dire que cela me semble assez étrange. Etant donné qu'il implémente déjà IEnumerable
, je ne vois pas pourquoi il ne devrait pas implémenter IEnumerable < T >
. Cela n'aurait pas beaucoup de sens d'implémenter IList < T >
, mais l'interface générique simple irait bien.
Si vous le souhaitez, vous pouvez appeler Cast < T >
(si vous utilisez .NET 3.5) ou écrire votre propre méthode pour parcourir le tableau. Pour éviter le casting, vous devez écrire votre propre méthode qui trouve les limites inférieure / supérieure de chaque dimension et récupère les éléments de cette façon. Pas terriblement agréable.
Autres conseils
Il existe une solution de contournement: vous pouvez convertir n’importe quel tableau multidimensionnel en un IEnumerable
public static class ArrayExtensions
{
public static IEnumerable<T> ToEnumerable<T>(this Array target)
{
foreach (var item in target)
yield return (T)item;
}
}
Les tableaux multidimensionnels ne sont pas aux fins de la hiérarchie d'héritage. Ils sont un type complètement séparé. De plus, ce type n’est pas bien supporté par le framework pour deux raisons possibles:
- Ce n'est pas vraiment utile. Les matrices sont la seule utilisation réelle des tableaux multidimensionnels. Pour presque tout le reste, d’autres structures de données (par exemple, des tableaux déchiquetés) sont mieux adaptées.
- Il n’est pas facile de concevoir des méthodes génériques et utiles pour ces structures.
Dans le cas de IEnumerable
, comment cela aurait-il dû être mis en œuvre, c'est-à-dire dans quel ordre les éléments devraient-ils être énumérés? Il n'y a pas d'ordre inhérent aux tableaux multidimensionnels.
Les tableaux à une dimension liés à zéro implémentent à la fois IEnumerable
et IEnumerable < T >
, mais les tableaux multidimensionnels, malheureusement, implémentent uniquement IEnumerable
. La "solution de contournement" by @Jader Dias convertit en effet un tableau multidimensionnel en IEnumerable < T >
mais avec un coût énorme: chaque élément d’un tableau sera encadré.
Voici une version qui ne causera pas la boxe pour chaque élément:
public static class ArrayExtensions
{
public static IEnumerable<T> ToEnumerable<T>(this T[,] target)
{
foreach (var item in target)
yield return item;
}
}
Les tableaux déchiquetés ne prennent pas en charge IEnumerable < int >
, car les structures multidimensionnelles ne sont pas vraiment un tableau de type, mais un tableau de tableau de type:
int[] singleDimensionArray = new int[10];
int[][] multiJagged = new int[10][];
Debug.WriteLine(singleDimensionArray is IEnumerable<int>);
Debug.WriteLine(multiJagged is IEnumerable<int[]>);
Debug.WriteLine(singleDimensionArray is IEnumerable);
Debug.WriteLine(multiJagged is IEnumerable);
Imprime true, true, true, true.
Remarque : int [,]
n'est pas un IEnumerable < int [] >
, c'est pour les raisons spécifiées dans l'autre réponse, à savoir il n'y a pas de moyen générique de savoir quelle dimension à itérer. Avec les tableaux en dents de scie, il n'y a pas autant de place pour l'interprétation car la syntaxe est assez claire: il s'agit d'un tableau de tableaux.
Pensez à l'inverse. Le tableau 2D existe déjà. Il suffit de l'énumérer. Créez un tableau 2D avec le score et l'emplacement d'un tableau ou de marques initiales, y compris les valeurs en double.
int[] secondmarks = {20, 15, 31, 34, 35, 50, 40, 90, 99, 100, 20};
IEnumerable<int> finallist = secondmarks.OrderByDescending(c => c);
int[,] orderedMarks = new int[2, finallist.Count()];
Enumerable.Range(0, finallist.Count()).ToList().ForEach(k => {orderedMarks[0, k] = (int) finallist.Skip(k).Take(1).Average();
orderedMarks[1, k] = k + 1;});
Enumerable.Range(0, finallist.Count()).Select(m => new {Score = orderedMarks[0, m], Place = orderedMarks[1, m]}).Dump();
Résultats:
Score Place
100 1
99 2
90 3
50 4
40 5
35 6
34 7
31 8
20 9
20 10
15 11