ArraySegment <T> sınıfının kullanımı nedir?


101

Sınıfı ArraySegment<byte>alt MessageEncodersınıfa ayırırken türe rastladım .

Şimdi anlıyorum, belirli bir dizinin bir parçası, bir ofset alıyor, numaralandırılamıyor ve bir indeksleyiciye sahip değil, ancak yine de kullanımını anlayamıyorum. Birisi lütfen bir örnekle açıklayabilir mi?


8
ArraySegmentNet 4.5'te numaralandırılabilir gibi görünüyor .
svick

Bu soru gibi girişim için ..
Ken Kin

Yanıtlar:


57

ArraySegment<T>.NET 4.5 + ve .NET Core'da şu anda uygulandığı için çok daha kullanışlı hale geldi :

  • IList<T>
  • ICollection<T>
  • IEnumerable<T>
  • IEnumerable
  • IReadOnlyList<T>
  • IReadOnlyCollection<T>

hiçbir arabirim uygulamayan .NET 4 sürümünün aksine .

Sınıf artık LINQ'nun harika dünyasında yer alabilir, böylece içerikleri sorgulama, orijinal diziyi etkilemeden içerikleri tersine çevirme, ilk öğeyi alma gibi olağan LINQ şeylerini yapabiliriz:

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
Açıklanamaz bir şekilde GetEnumeratorözel hale getirilmiş olsalar da , IEnumerable<T>onu aramak için (bir boks dönüşümü) yapmanız gerektiği anlamına gelir . Ugh!
BlueRaja - Dany Pflughoeft

27
  1. IO sınıfları için tampon bölümleme - Eşzamanlı okuma ve yazma işlemleri için aynı tamponu kullanın ve tüm işleminizi açıklayan tek bir yapıya sahip olun.
  2. Set Functions - Matematiksel olarak bakıldığında, bu yeni yapıyı kullanarak herhangi bir bitişik alt kümeyi temsil edebilirsiniz. Bu, temelde dizinin bölümlerini oluşturabileceğiniz anlamına gelir, ancak tüm olasılıkları ve eşitleri söyleyemezsiniz. The1 tarafından önerilen telefon teaserının ArraySegment bölümleme ve bir ağaç yapısı kullanılarak zarif bir şekilde çözülebileceğini unutmayın. Son sayılar, önce ağaç derinliğini geçerek yazılabilirdi. Bu bellek ve hız açısından ideal bir senaryo olurdu inanıyorum.
  3. Çoklu okuma - Kontrol kapısı olarak bölümlere ayrılmış dizileri kullanırken aynı veri kaynağı üzerinde çalışmak için artık birden çok iş parçacığı üretebilirsiniz. Ayrık hesaplamaları kullanan döngüler artık oldukça kolay bir şekilde çıkarılabilir; bu, en son C ++ derleyicilerinin bir kod optimizasyonu adımı olarak yapmaya başladığı bir şey.
  4. UI Segmentation - Segmentli yapılar kullanarak UI ekranlarınızı kısıtlayın. Artık, görüntüleme işlevlerine hızla uygulanabilen veri sayfalarını temsil eden yapıları saklayabilirsiniz. Doğrusal bir veri deposunu düğüm toplama segmentlerine bölerek, ayrı görünümleri veya hatta bir TreeView'daki düğümler gibi hiyerarşik yapıları görüntülemek için tek bitişik diziler kullanılabilir.

Bu örnekte, orijinal diziyi, Offset ve Count özelliklerini nasıl kullanabileceğinizi ve ayrıca ArraySegment'te belirtilen öğeler arasında nasıl döngü oluşturabileceğinizi inceliyoruz.

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 Yapısı - ne düşünüyorlardı?


3
ArraySegment sadece bir yapıdır. En iyi tahminim, amacının bir dizinin bir parçasının, bir kopyasını almak zorunda kalmadan etrafta dolaşmasına izin vermek olduğu.
Brian

1
For döngüsünün koşul ifadesinin olması gerektiğine inanıyorum i < segment.Offset + segment.Count.
Eren Ersönmez

1
Bahsettiğiniz gerçekler için +1 ama @Eren haklı: Bir segmentin bu gibi öğelerini yineleyemezsiniz.
Şafak Gür

3
Başka birinin kodunu kullandığınızda atıfta bulunmak genellikle uygundur. Bu sadece görgü kuralları. Sizin örnek kaynaklanır dotnetperls.com/arraysegment .

1
Tabii cevabınızdan ödünç almadılarsa. Bu durumda size kredi vermeleri gerekir. :)

27

Bu, bir diziye referans tutmaktan başka hiçbir şey yapmayan ve bir dizin aralığını depolayan küçük, küçük bir asker yapısıdır. Biraz tehlikeli, dizi verilerinin bir kopyasını oluşturmamasına ve diziyi hiçbir şekilde değişmez yapmamasına veya değişmezlik ihtiyacını ifade etmemesine dikkat edin. Daha tipik programlama modeli, .NET BeginRead () yöntemlerinde, String.SubString (), Encoding.GetString (), vb.

Web soketlerinde çalışan ve WCF'den hoşlanan belirli bir Microsoft programcısı gibi görünen şey dışında, .NET Framework içinde fazla kullanılmaz. Muhtemelen doğru rehberlik budur, eğer hoşunuza giderse onu kullanın. NET 4.6'da bir gözetleme yaptı, eklenen MemoryStream. TryGetBuffer () yöntemi bunu kullanır. outSanırım iki argümana sahip olmak yerine tercih edilir .

Genel olarak, daha evrensel dilim kavramı, Mads Torgersen ve Stephen Toub gibi ana .NET mühendislerinin istek listesinin üst sıralarında yer alır. İkincisi, array[:]sözdizimi önerisini bir süre önce başlattı , bu Roslyn sayfasında ne düşündüklerini görebilirsiniz . CLR desteği almanın sonuçta buna bağlı olduğunu varsayıyorum. Bu aktif olarak C # sürüm 7 afaik için düşünülüyor, gözünüzü System.Slices üzerinde tutun .

Güncelleme: ölü bağlantı, bu 7.2 sürümünde Span olarak gönderildi .

Güncelleme2: Aralık ve Dizin türleri ve Slice () yöntemi ile C # sürüm 8.0'da daha fazla destek.


'Bu çok kullanışlı değildir - ne yazık ki Bellek sınırlaması nedeniyle mikro optimizasyonlar gerekenden bir sistemde inanılmaz yararlı bulunan bilgi vardır. Ayrıca , diğer 'tipik' çözüm kendi programını uzaklaştıran etmez
AaronHS

5
Tamam, tamam, onu kullanma alışkanlığı olan herkesin bir tanıklığına gerçekten ihtiyacım yok :) @ CRice'nin yorumuna destek vermek en iyisi. Belirtildiği gibi, "beğenirseniz kullanın". Öyleyse kullan. Dilimler harika olacak, sabırsızlanıyorum.
Hans Passant

Dışarıdaki değişmez safçılar için bir ReadOnlySpan var.
Arek Bal

ArraySegmentindeksleyiciye sahiptir Memoryve Spanbu konuda tamamen sessizdir.
astrowalker

7

Bir sarmalayıcı sınıfına ne dersiniz? Verilerin geçici tamponlara kopyalanmasını önlemek için.

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

Misal:

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

Çıktı:

3
4

3
4

Çok yararlı dostum, teşekkürler, not bunu uygulamak yapabilirsiniz IEnumerable<T>IEnumerator sonra ekleyinIEnumerable.GetEnumerator() { return GetEnumerator(); }
Maya

5

ArraySegment, düşündüğünüzden ÇOK daha kullanışlıdır. Aşağıdaki birim testini çalıştırmayı deneyin ve şaşırmaya hazırlanın!

    [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("}");
        }
    }

Görüyorsunuz, yapmanız gereken tek şey IList'e bir ArraySegment atamak ve muhtemelen ilk etapta yapmasını beklediğiniz her şeyi yapacak. Normal bir liste gibi davranmasına rağmen türün hala ArraySegment olduğuna dikkat edin.

ÇIKTI:

ArraySegment0 {0,1,2,}
ArraySegment1 {3,4,5,}
ArraySegment2 {6,7,8,}

4
Yazmak için gerekli olması üzücü IList<T>. İndeksleyicinin olmasını beklerdim public.
xmedeko

2
Bu cevaba ulaşan ve bunun mucize bir çözüm olduğunu düşünen herkes için, öncelikle performans ihtiyaçlarınızı göz önünde bulundurmanızı ve bunu, dizi segmentindeki dizin kısıtlamalarını kullanarak orijinal diziye doğrudan erişimle karşılaştırmanızı öneririm. Bir IList'e çevrim, uygulamaya ulaşmadan önce IList arayüzünden atlamak için sonraki yöntem çağrılarının (indeksleyici dahil) gerektirir. İnternette, soyutlanmış aramaları sıkı döngülerde kullanmanın performans maliyeti hakkında insanların konuştuğu birçok tartışma var. Burada okuyun: github.com/dotnet/coreclr/issues/9105
JamesHoux

3

Basit bir deyişle: Bir diziye referansta bulunur ve tek bir dizi değişkenine her biri farklı bir aralığa sahip birden çok referansa sahip olmanıza olanak tanır.

Aslında, başlangıç ​​dizinini ve uzunluğunu tutmak için birden çok değişkene sahip olmak yerine bir dizinin bölümlerini daha yapılandırılmış bir şekilde kullanmanıza ve geçirmenize yardımcı olur. Ayrıca koleksiyon arayüzlerinin dizi bölümleriyle daha kolay çalışmasını sağlar.

Örneğin, aşağıdaki iki kod örneği aynı şeyi yapar, biri ArraySegment ile diğeri olmadan:

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

ve,

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

Açıkçası, ilk kod parçacığı, özellikle dizi bölümlerini bir işleve geçirmek istediğinizde daha çok tercih edilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.