クラスArraySegment<byte>
をサブクラス化しているときに型を見つけましたMessageEncoder
。
私はそれが特定の配列のセグメントであり、オフセットを取り、列挙可能ではなく、インデクサーがないことを理解しましたが、それでもその使用法を理解できません。誰かが例で説明できますか?
クラスArraySegment<byte>
をサブクラス化しているときに型を見つけましたMessageEncoder
。
私はそれが特定の配列のセグメントであり、オフセットを取り、列挙可能ではなく、インデクサーがないことを理解しましたが、それでもその使用法を理解できません。誰かが例で説明できますか?
回答:
ArraySegment<T>
.NET 4.5 +と.NET Coreでは、次のものを実装することで、より便利になりました。
IList<T>
ICollection<T>
IEnumerable<T>
IEnumerable
IReadOnlyList<T>
IReadOnlyCollection<T>
インターフェースをまったく実装していない.NET 4バージョンとは対照的です。
これで、クラスはLINQのすばらしい世界に参加できるようになり、コンテンツのクエリ、元の配列に影響を与えずにコンテンツを逆にする、最初のアイテムを取得するなど、通常のLINQを実行できます。
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
GetEnumerator
非公開にされていますが、IEnumerable<T>
それを呼び出すには(ボクシング変換)にキャストする必要があります。うわっ!
- IOクラスのバッファー分割-読み取りと書き込みの同時操作に同じバッファーを使用し、操作全体を記述できる単一の構造を渡します。
- 関数の設定-数学的に言えば、この新しい構造を使用して、連続するサブセットを表すことができます。これは基本的に配列のパーティションを作成できることを意味しますが、すべての確率とすべての偶数を表すことはできません。The1によって提案された電話ティーザーは、ArraySegmentパーティショニングとツリー構造を使用してエレガントに解決できた可能性があることに注意してください。最終的な数値は、最初にツリーの深さをトラバースすることで書き出すことができます。これは、メモリと速度の点で理想的なシナリオでした。
- マルチスレッディング-セグメント化された配列をコントロールゲートとして使用しながら、複数のスレッドを生成して同じデータソースを操作できるようになりました。離散計算を使用するループを非常に簡単に作り出すことができます。これは、最新のC ++コンパイラがコード最適化ステップとして実行し始めているものです。
- UIセグメンテーション-セグメント化された構造を使用してUI表示を制限します。これで、表示機能にすばやく適用できるデータのページを表す構造を保存できます。線形データストアをノードコレクションセグメントにセグメント化することで、個別のビュー、またはTreeViewのノードなどの階層構造を表示するために、単一の連続した配列を使用できます。
この例では、元の配列、OffsetプロパティおよびCountプロパティを使用する方法と、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]);
}
}
}
i < segment.Offset + segment.Count
。
これは、配列への参照を保持し、インデックスの範囲を格納する以外に何もしない、ちっぽけな小さな兵士の構造体です。少し危険です。配列データのコピーを作成せず、配列を不変にしたり、不変性の必要性を表現したりしないことに注意してください。より一般的なプログラミングパターンは、.NET BeginRead()メソッド、String.SubString()、Encoding.GetString()などで行われるように、配列と長さ変数またはパラメーターを保持または渡すだけです。
.NET Framework内ではあまり使用されませんが、Webソケットとそれに好意的なWCFに取り組んだ特定のMicrosoftプログラマーのように見える場合を除きます。これはおそらく適切なガイダンスですが、気に入った場合はそれを使用してください。これは.NET 4.6でプレビューを行いました。追加されたMemoryStream.TryGetBuffer()メソッドがそれを使用します。out
私が仮定する2つの引数よりも優先されます。
一般的に、より普遍的なスライスの概念は、Mads TorgersenやStephen Toubのような主要な.NETエンジニアのウィッシュリストで高く評価されています。後者array[:]
は少し前に構文の提案を開始しました。Roslynのこのページで、彼らが何を考えているかを確認できます。CLRサポートを取得することが、これが最終的に左右されるものだと思います。これは、積極的にあなたの目を保つ、私の知る限りでC#バージョン7のためにについて考えているSystem.Slices。
更新:リンク切れ、これはバージョン7.2でSpanとして出荷されました。
Update2:RangeおよびIndexタイプとSlice()メソッドを使用したC#バージョン8.0でのサポートの強化。
ラッパークラスはどうですか?データを一時バッファにコピーしないようにするためだけです。
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
例:
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);
}
出力:
3
4
3
4
IEnumerable<T>
からIEnumeratorを追加できることに注意してくださいIEnumerable.GetEnumerator() { return GetEnumerator(); }
ArraySegmentは、思ったよりもずっと便利です。次の単体テストを実行して、驚かれる準備をしてください!
[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("}");
}
}
ご覧のとおり、行う必要があるのは、ArraySegmentをIListにキャストすることだけです。これにより、そもそも予想していたことのすべてが実行されます。通常のリストのように動作しているにもかかわらず、型はまだArraySegmentであることに注意してください。
出力:
ArraySegment0 {0,1,2,}
ArraySegment1 {3,4,5,}
ArraySegment2 {6,7,8,}
IList<T>
です。インデクサーはだと思いますpublic
。
簡単に言うと、それは配列への参照を保持し、それぞれが異なる範囲を持つ単一の配列変数への複数の参照を持つことを可能にします。
実際、これは、開始インデックスと長さを保持するために、複数の変数を持つ代わりに、より構造化された方法で配列のセクションを使用して渡すのに役立ちます。また、配列セクションをより簡単に操作するためのコレクションインターフェイスを提供します。
たとえば、次の2つのコード例は同じことを行い、1つはArraySegmentを使用し、もう1つは使用しません。
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());
そして、
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());
明らかに配列のセグメントを関数に渡したい場合は特に、最初のコードスニペットがより好ましいです。
ArraySegment
.Net 4.5で列挙可能のようです。