Je suis juste tombé sur le ArraySegment<byte>
tapez tout en sous-classant la classe MessageEncoder
.
Je comprends maintenant que c'est un segment d'un tableau donné, prend un décalage, n'est pas énumérable et n'a pas d'indexeur, mais je n'arrive toujours pas à comprendre son utilisation. Quelqu'un peut-il expliquer avec un exemple?
ArraySegment<T>
est devenu un beaucoup plus utile dans .NET 4.5 car il implémente maintenant:
IList<T>
ICollection<T>
IEnumerable<T>
IEnumerable
IReadOnlyList<T>
IReadOnlyCollection<T>
par opposition à la version .NET 4 qui n'implémentait aucune interface.
La classe est maintenant en mesure de participer au monde merveilleux de LINQ afin que nous puissions faire les choses habituelles de LINQ comme interroger le contenu, inverser le contenu sans affecter le tableau d'origine, obtenir le premier élément, etc.:
var array = new byte[] { 5, 8, 9, 20, 70, 44, 2, 4 };
array.Dump();
var segment = new ArraySegment<byte>(array, 2, 3);
segment.Dump(); // output: 9, 20, 70
segment.Reverse().Dump(); // output 70, 20, 9
segment.Any(s => s == 99).Dump(); // output false
segment.First().Dump(); // output 9
array.Dump(); // no change
- Partitionnement de la mémoire tampon pour les classes IO - Utilisez la même mémoire tampon pour les opérations de lecture et d'écriture simultanées et disposez d'une structure unique que vous pouvez transmettre pour décrire l'ensemble de votre opération.
- Définir des fonctions - Mathématiquement parlant, vous pouvez représenter n'importe quel sous-ensemble contigu en utilisant cette nouvelle structure. Cela signifie essentiellement que vous pouvez créer des partitions du tableau, mais vous ne pouvez pas représenter toutes les cotes et tous les événements. Notez que le teaser de téléphone proposé par The1 aurait pu être résolu avec élégance en utilisant le partitionnement ArraySegment et une structure arborescente. Les nombres finaux auraient pu être écrits en parcourant d'abord la profondeur de l'arbre. Cela aurait été un scénario idéal en termes de mémoire et de vitesse je crois.
- Multithreading - Vous pouvez désormais générer plusieurs threads pour fonctionner sur la même source de données tout en utilisant des tableaux segmentés comme porte de contrôle. Les boucles qui utilisent des calculs discrets peuvent désormais être développées assez facilement, ce que les derniers compilateurs C++ commencent à faire comme étape d'optimisation de code.
- Segmentation de l'interface utilisateur: contraignez vos affichages de l'interface utilisateur à l'aide de structures segmentées. Vous pouvez désormais stocker des structures représentant des pages de données qui peuvent être rapidement appliquées aux fonctions d'affichage. Des tableaux contigus uniques peuvent être utilisés afin d'afficher des vues discrètes, ou même des structures hiérarchiques telles que les nœuds dans un TreeView en segmentant un magasin de données linéaire en segments de collection de nœuds.
Dans cet exemple, nous examinons comment vous pouvez utiliser le tableau d'origine, les propriétés Offset et Count, et comment vous pouvez parcourir les éléments spécifiés dans l'ArraySegment.
using System;
class Program
{
static void Main()
{
// Create an ArraySegment from this array.
int[] array = { 10, 20, 30 };
ArraySegment<int> segment = new ArraySegment<int>(array, 1, 2);
// Write the array.
Console.WriteLine("-- Array --");
int[] original = segment.Array;
foreach (int value in original)
{
Console.WriteLine(value);
}
// Write the offset.
Console.WriteLine("-- Offset --");
Console.WriteLine(segment.Offset);
// Write the count.
Console.WriteLine("-- Count --");
Console.WriteLine(segment.Count);
// Write the elements in the range specified in the ArraySegment.
Console.WriteLine("-- Range --");
for (int i = segment.Offset; i < segment.Count+segment.Offset; i++)
{
Console.WriteLine(segment.Array[i]);
}
}
}
C'est une petite structure de soldat chétive qui ne fait que garder une référence à un tableau et stocke une plage d'index. Un peu dangereux, sachez qu'il ne fait pas de copie des données du tableau et ne rend en aucun cas le tableau immuable ou exprime le besoin d'immuabilité. Le modèle de programmation le plus typique consiste à simplement conserver ou transmettre le tableau et une variable de longueur ou un paramètre, comme cela se fait dans les méthodes .NET BeginRead (), String.SubString (), Encoding.GetString (), etc., etc.
Il n'obtient pas beaucoup d'utilisation à l'intérieur du .NET Framework, à l'exception de ce qui semble être un programmeur Microsoft particulier qui a travaillé sur les sockets Web et WCF l'aimant. C'est probablement le bon guide, si vous l'aimez, utilisez-le. Il a fait un coup d'œil dans .NET 4.6, la méthode MemoryStream.TryGetBuffer () ajoutée l'utilise. Je préfère avoir deux arguments out
je suppose.
En général, la notion plus universelle de tranches figure en bonne place sur la liste de souhaits des principaux ingénieurs .NET comme Mads Torgersen et Stephen Toub. Ce dernier a lancé le array[:]
proposition de syntaxe il y a quelque temps, vous pouvez voir à quoi ils ont pensé dans cette page de Roslyn . Je suppose que l'obtention du soutien CLR est ce sur quoi cela repose en fin de compte. Ceci est activement pensé pour afaik C # version 7, gardez un œil sur System.Slices .
Mise à jour: lien mort, fourni dans la version 7.2 sous la forme Span .
Update2: plus de support dans C # version 8.0 avec les types Range et Index et une méthode Slice ().
Qu'en est-il d'une classe wrapper? Juste pour éviter de copier des données dans des tampons temporels.
public class SubArray<T> {
private ArraySegment<T> segment;
public SubArray(T[] array, int offset, int count) {
segment = new ArraySegment<T>(array, offset, count);
}
public int Count {
get { return segment.Count; }
}
public T this[int index] {
get {
return segment.Array[segment.Offset + index];
}
}
public T[] ToArray() {
T[] temp = new T[segment.Count];
Array.Copy(segment.Array, segment.Offset, temp, 0, segment.Count);
return temp;
}
public IEnumerator<T> GetEnumerator() {
for (int i = segment.Offset; i < segment.Offset + segment.Count; i++) {
yield return segment.Array[i];
}
}
} //end of the class
Exemple:
byte[] pp = new byte[] { 1, 2, 3, 4 };
SubArray<byte> sa = new SubArray<byte>(pp, 2, 2);
Console.WriteLine(sa[0]);
Console.WriteLine(sa[1]);
//Console.WriteLine(b[2]); exception
Console.WriteLine();
foreach (byte b in sa) {
Console.WriteLine(b);
}
Sortie:
3
4
3
4
Le ArraySegment est BEAUCOUP plus utile que vous ne le pensez. Essayez d'exécuter le test unitaire suivant et préparez-vous à être étonné!
[TestMethod]
public void ArraySegmentMagic()
{
var arr = new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
var arrSegs = new ArraySegment<int>[3];
arrSegs[0] = new ArraySegment<int>(arr, 0, 3);
arrSegs[1] = new ArraySegment<int>(arr, 3, 3);
arrSegs[2] = new ArraySegment<int>(arr, 6, 3);
for (var i = 0; i < 3; i++)
{
var seg = arrSegs[i] as IList<int>;
Console.Write(seg.GetType().Name.Substring(0, 12) + i);
Console.Write(" {");
for (var j = 0; j < seg.Count; j++)
{
Console.Write("{0},", seg[j]);
}
Console.WriteLine("}");
}
}
Vous voyez, tout ce que vous avez à faire est de lancer un ArraySegment sur IList et il fera tout ce que vous attendiez probablement de lui en premier lieu. Notez que le type est toujours ArraySegment, même s'il se comporte comme une liste normale.
SORTIE:
ArraySegment0 {0,1,2,}
ArraySegment1 {3,4,5,}
ArraySegment2 {6,7,8,}
En termes simples: il conserve la référence à un tableau, vous permettant d'avoir plusieurs références à une seule variable de tableau, chacune avec une plage différente.
En fait, il vous aide à utiliser et à passer des sections d'un tableau de manière plus structurée, au lieu d'avoir plusieurs variables, pour conserver l'index de début et la longueur. Il fournit également des interfaces de collecte pour travailler plus facilement avec les sections de tableau.
Par exemple, les deux exemples de code suivants font la même chose, l'un avec ArraySegment et l'autre sans:
byte[] arr1 = new byte[] { 1, 2, 3, 4, 5, 6 };
ArraySegment<byte> seg1 = new ArraySegment<byte>(arr1, 2, 2);
MessageBox.Show((seg1 as IList<byte>)[0].ToString());
et,
byte[] arr1 = new byte[] { 1, 2, 3, 4, 5, 6 };
int offset = 2;
int length = 2;
byte[] arr2 = arr1;
MessageBox.Show(arr2[offset + 0].ToString());
De toute évidence, le premier extrait de code est préférable, en particulier lorsque vous souhaitez passer des segments de tableau à une fonction.