การใช้คลาส ArraySegment <T> คืออะไร?


101

ฉันเพิ่งเจอArraySegment<byte>ประเภทในขณะที่คลาสย่อยของMessageEncoderชั้นเรียน

ตอนนี้ฉันเข้าใจแล้วว่ามันเป็นเซ็กเมนต์ของอาร์เรย์ที่กำหนดใช้ค่าออฟเซ็ตไม่สามารถนับได้และไม่มีตัวสร้างดัชนี แต่ฉันยังไม่เข้าใจการใช้งาน ใครช่วยอธิบายด้วยตัวอย่างได้ไหม


8
ดูเหมือนว่าArraySegmentจะแจกแจงได้ใน. Net 4.5
svick

สำหรับความพยายามเช่นคำถามนี้ ..
Ken Kin

คำตอบ:


57

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

4
แม้ว่าพวกเขาจะสร้างความเป็นGetEnumeratorส่วนตัวอย่างอธิบายไม่ได้แต่หมายความว่าคุณถูกบังคับให้เลือกIEnumerable<T>(แปลงชกมวย) เพื่อเรียกสิ่งนี้ ฮึ
BlueRaja - Danny Pflughoeft

28

มันเป็นโครงสร้างทหารตัวน้อยที่ไม่ได้ทำอะไรเลยนอกจากเก็บข้อมูลอ้างอิงไปยังอาร์เรย์และเก็บช่วงดัชนี อันตรายเล็กน้อยโปรดระวังว่าจะไม่ทำสำเนาข้อมูลอาร์เรย์และไม่ทำให้อาร์เรย์ไม่เปลี่ยนรูปหรือแสดงความจำเป็นในการไม่เปลี่ยนรูปในทางใดทางหนึ่ง รูปแบบการเขียนโปรแกรมทั่วไปมากขึ้นคือเก็บหรือส่งอาร์เรย์และตัวแปรความยาวหรือพารามิเตอร์เช่นเดียวกับที่ทำในเมธอด. NET BeginRead (), String.SubString (), Encoding.GetString () ฯลฯ เป็นต้น

NET Framework ไม่ได้รับประโยชน์มากนักยกเว้นสิ่งที่ดูเหมือนว่าโปรแกรมเมอร์ Microsoft คนหนึ่งที่ทำงานบนเว็บซ็อกเก็ตและ WCF ชอบ ซึ่งน่าจะเป็นคำแนะนำที่เหมาะสมถ้าคุณชอบก็ใช้มัน มันทำ peek-a-boo ใน. NET 4.6 ซึ่งเป็นเมธอด MemoryStream.TryGetBuffer () ที่เพิ่มเข้ามาใช้ ต้องการมากกว่ามีสองoutข้อโต้แย้งที่ฉันคิด

โดยทั่วไปแล้วแนวคิดที่เป็นสากลมากขึ้นของชิ้นส่วนนั้นเป็นที่ต้องการของวิศวกรหลัก. NET เช่น Mads Torgersen และ Stephen Toub หลังเตะออกarray[:]ไวยากรณ์ข้อเสนอในขณะที่ที่ผ่านมาคุณจะเห็นสิ่งที่พวกเขาได้รับความคิดเกี่ยวกับในหน้านี้โรสลิน ฉันคิดว่าการได้รับการสนับสนุน CLR คือสิ่งที่เกิดขึ้นในท้ายที่สุด นี้เป็นงานที่ได้รับการคิดเกี่ยวกับการสำหรับ C # 7 รุ่น AFAIK ให้ตาของคุณในSystem.Slices

ปรับปรุง: ตายลิงก์นี้ส่งในรุ่น 7.2 เป็นช่วง

Update2: การสนับสนุนเพิ่มเติมใน C # เวอร์ชัน 8.0 พร้อมประเภท Range และ Index และวิธี Slice ()


"มันไม่ได้มีประโยชน์มาก - ผมพบว่ามันมีประโยชน์อย่างไม่น่าเชื่อในระบบที่ต้องโชคร้ายที่การเพิ่มประสิทธิภาพไมโครเนื่องจากข้อ จำกัด ของหน่วยความจำความจริงที่ว่ามี. นอกจากนี้ยังมีอื่น ๆ '' การแก้ปัญหาโดยทั่วไปไม่ได้เอาออกจากยูทิลิตี้
AaronHS

5
โอเคโอเคฉันไม่ต้องการคำรับรองจากทุกคนที่มีนิสัยชอบใช้ :) ดีที่สุดที่จะโหวตความคิดเห็นของ @ CRice ตามที่ระบุไว้ "ถ้าชอบก็ใช้". ดังนั้นใช้มัน ชิ้นจะยอดเยี่ยมรอไม่ไหวแล้ว
Hans Passant

มี ReadOnlySpan สำหรับนักปราชญ์ที่ไม่เปลี่ยนรูปเหล่านั้น
Arek Bal

ArraySegmentมีตัวสร้างดัชนีในขณะที่MemoryและSpanเงียบเกี่ยวกับเรื่องนี้
astrowalker

27
  1. การแบ่งส่วนบัฟเฟอร์สำหรับคลาส IO - ใช้บัฟเฟอร์เดียวกันสำหรับการดำเนินการอ่านและเขียนพร้อมกันและมีโครงสร้างเดียวที่คุณสามารถส่งผ่านอธิบายการดำเนินการทั้งหมดของคุณได้
  2. ตั้งค่าฟังก์ชัน - การพูดทางคณิตศาสตร์คุณสามารถแสดงชุดย่อยที่ต่อเนื่องกันโดยใช้โครงสร้างใหม่นี้ โดยพื้นฐานแล้วหมายความว่าคุณสามารถสร้างพาร์ติชันของอาร์เรย์ได้ แต่คุณไม่สามารถแสดงถึงราคาทั้งหมดและคู่ทั้งหมดได้ โปรดทราบว่าทีเซอร์โทรศัพท์ที่เสนอโดย The1 สามารถแก้ไขได้อย่างสวยงามโดยใช้การแบ่งพาร์ติชัน ArraySegment และโครงสร้างแบบต้นไม้ ตัวเลขสุดท้ายสามารถเขียนได้โดยการสำรวจความลึกของต้นไม้ก่อน นี่คงเป็นสถานการณ์ในอุดมคติในแง่ของหน่วยความจำและความเร็วที่ฉันเชื่อ
  3. มัลติเธรด - ขณะนี้คุณสามารถวางไข่หลายเธรดเพื่อทำงานบนแหล่งข้อมูลเดียวกันได้ในขณะที่ใช้อาร์เรย์ที่แบ่งส่วนเป็นเกตควบคุม ลูปที่ใช้การคำนวณแบบแยกส่วนสามารถทำฟาร์มได้อย่างง่ายดายซึ่งเป็นสิ่งที่คอมไพเลอร์ C ++ ล่าสุดเริ่มทำเป็นขั้นตอนการเพิ่มประสิทธิภาพโค้ด
  4. การแบ่งส่วน UI - จำกัด การแสดง UI ของคุณโดยใช้โครงสร้างแบบแบ่งส่วน ตอนนี้คุณสามารถจัดเก็บโครงสร้างที่แสดงหน้าข้อมูลที่สามารถนำไปใช้กับฟังก์ชันการแสดงผลได้อย่างรวดเร็ว สามารถใช้อาร์เรย์เดี่ยวที่ต่อเนื่องกันเพื่อแสดงมุมมองที่ไม่ต่อเนื่องหรือแม้กระทั่งโครงสร้างแบบลำดับชั้นเช่นโหนดใน TreeView โดยการแบ่งส่วนที่เก็บข้อมูลเชิงเส้นออกเป็นส่วนการรวบรวมโหนด

ในตัวอย่างนี้เราจะดูวิธีที่คุณสามารถใช้อาร์เรย์ดั้งเดิมคุณสมบัติออฟเซ็ตและจำนวนรวมถึงวิธีที่คุณสามารถวนซ้ำองค์ประกอบที่ระบุใน 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]);
        }
    }
}

โครงสร้าง ArraySegment - พวกเขาคิดอะไรอยู่?


3
ArraySegment เป็นเพียงโครงสร้าง การคาดเดาที่ดีที่สุดของฉันคือจุดประสงค์คือเพื่อให้สามารถส่งผ่านส่วนของอาร์เรย์ได้โดยไม่ต้องทำสำเนา
Brian

1
i < segment.Offset + segment.Countผมเชื่อว่าคำสั่งเงื่อนไขของการห่วงควรจะเป็น
Eren Ersönmez

1
+1 สำหรับข้อเท็จจริงที่คุณกล่าวถึง แต่ @Eren ถูกต้อง: คุณไม่สามารถทำซ้ำองค์ประกอบของกลุ่มเช่นนั้น
ŞafakGür

3
โดยปกติแล้วการระบุแหล่งที่มาเมื่อคุณใช้รหัสของใครบางคนก็เหมาะสม เป็นเพียงมารยาทที่ดี มีต้นกำเนิดจากตัวอย่างของคุณdotnetperls.com/arraysegment

1
พวกเขายืมจากคำตอบของคุณเว้นแต่แน่นอน ในกรณีนี้พวกเขาควรให้เครดิตคุณ :)

7

คลาส Wrapper คืออะไร? เพียงเพื่อหลีกเลี่ยงการคัดลอกข้อมูลไปยังบัฟเฟอร์ชั่วคราว

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);
}

Ouput:

3
4

3
4

เพื่อนที่มีประโยชน์มากขอบคุณโปรดทราบว่าคุณสามารถใช้งานได้IEnumerable<T>จากนั้นเพิ่ม IEnumeratorIEnumerable.GetEnumerator() { return GetEnumerator(); }
MaYaN

5

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,}

4
IList<T>ก็สงสารก็จำเป็นที่จะโยนมันทิ้งไป publicผมคาดว่าดัชนีจะเป็น
xmedeko

2
สำหรับใครก็ตามที่ใช้คำตอบนี้และคิดว่ามันเป็นทางออกที่มหัศจรรย์ฉันขอแนะนำให้พิจารณาความต้องการด้านประสิทธิภาพของคุณก่อนและเปรียบเทียบสิ่งนี้ก่อนเปรียบเทียบกับการเข้าถึงอาร์เรย์ดั้งเดิมโดยตรงโดยใช้ข้อ จำกัด ของดัชนีจากส่วนอาร์เรย์ การส่งไปยัง IList จำเป็นต้องมีการเรียกใช้เมธอดในภายหลัง (รวมถึงตัวสร้างดัชนี) เพื่อข้ามผ่านอินเทอร์เฟซ IList ก่อนที่จะถึงการนำไปใช้ มีการอภิปรายจำนวนมากบนอินเทอร์เน็ตที่ผู้คนพูดถึงต้นทุนประสิทธิภาพของการใช้การโทรแบบนามธรรมในลูปที่แน่น อ่านที่นี่: github.com/dotnet/coreclr/issues/9105
JamesHoux

3

พูดง่ายๆก็คือมันจะอ้างอิงไปยังอาร์เรย์ช่วยให้คุณมีการอ้างอิงหลายรายการไปยังตัวแปรอาร์เรย์เดียวโดยแต่ละตัวมีช่วงที่แตกต่างกัน

ในความเป็นจริงมันช่วยให้คุณใช้และส่งผ่านส่วนต่างๆของอาร์เรย์ในรูปแบบที่มีโครงสร้างมากขึ้นแทนที่จะมีตัวแปรหลายตัวเพื่อเก็บดัชนีและความยาวเริ่มต้น นอกจากนี้ยังมีอินเทอร์เฟซการรวบรวมเพื่อให้ทำงานกับส่วนอาร์เรย์ได้ง่ายขึ้น

ตัวอย่างเช่นตัวอย่างโค้ดสองตัวอย่างต่อไปนี้ทำสิ่งเดียวกันหนึ่งรายการมี ArraySegment และอีกอันไม่มี:

        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());

เห็นได้ชัดว่าข้อมูลโค้ดแรกเป็นที่ต้องการมากกว่าโดยเฉพาะเมื่อคุณต้องการส่งผ่านส่วนอาร์เรย์ไปยังฟังก์ชัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.