İlkay İlknur

ArraySegment<T> ve StringSegment Nedir? Nasıl Kullanılır?

Haziran 15, 2020

Önceki yazılarımızda Span ve Memory tiplerinden bahsetmiştik. Bu yazıda da bu tiplerden biraz daha eski olan ve benzer amaçlarla kullanabileceğimiz ArraySegment ve StringSegment'ten bahsedeceğiz.

ArraySegment basit olarak yine bir array'in belirli bir bölümünü temsil eden bir struct. Bu yapıyı Span'den farklı olarak bir wrapper olarak düşünebiliriz. ArraySegment aynı zamanda ICollection, IEnumerable, IList, IReadOnlyCollection, IReadOnlyList interfacelerini de implemente ediyor.

Varolan bir array üzerinde bir ArraySegment'i şu şekilde yaratabiliriz.

var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
ArraySegment<int> segment = new ArraySegment<int>(array, 2, 3);
//segment : 3,4,5

ArraySegment yaratırken kullandığımız parametreler orjinal arrayin kendisi, segmentin hangi offsetten başlayacağı ve ne kadar uzunlukta olacağı. ArraySegmenti yarattıktan sonra o tip üzerinden orjinal arraye, offset ve uzunluk değerlerine de ulaşmak mümkün.

var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
ArraySegment<int> segment = new ArraySegment<int>(array, 2, 3);
//segment : 3,4,5
 
Console.WriteLine(segment.Offset);
//2
Console.WriteLine(segment.Count);
//3
Console.WriteLine(segment.Array.Length);
//10

ArraySegment içerisinde bulunan Array propertysi her zaman orjinal array'i gösterir. Yani ArraySegment kullanarak array üzerinde yaptığınız işlemler doğrudan orjinal array üzerinde yapılır. Bunu gözönünde bulundurmamızda fayda var. Eğer böyle olmasını istemiyorsanız arrayin bir kopyasını alıp ilerlemeniz gerekir.

var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
ArraySegment<int> segment = new ArraySegment<int>(array, 2, 3);
//segment : 3,4,5
segment[0] = 10;
 
Console.Write(array[2]);
//10

ArraySegment'in en önemli özelliklerinden biri de LINQ querylerini çalıştırabiliyor olmamız.

var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
ArraySegment<int> segment = new ArraySegment<int>(array, 2, 3);
////segment : 3,4,5
Console.WriteLine(segment.Max());
//5

ArraySegment'de de Span ve Memory tipleri gibi slicingi supportu bulunmakta.

var array = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
ArraySegment<int> segment = new ArraySegment<int>(array, 2, 3);
////segment : 3,4,5
var newSegment = segment.Slice(1, 1);
//newSegment: 4

İki ArraySegment<T> instance'ının aynı olabilmesi için aynı arrayi göstermeleri, aynı offsetten başlamaları ve eşiş sayıda eleman içermeleri gerekiyor.

StringSegment

StringSegment tipi de aynı ArraySegment'te olduğu stringin farklı bölgelerini belirten bir struct. Bu structı kullanarak string.Substring metodunu kullanmadan string üzerindeki belirli bir bölge üzerinde işlem yapabiliriz. Böylece yeni bir string yaratarak allocationa neden olmadan işlemleri gerçekleştirebiliriz.

string sample = "test:0:stringsegment";
StringSegment first = new StringSegment(sample, 0, sample.IndexOf(':'));
//first: test
StringSegment last = new StringSegment(sample, sample.LastIndexOf(':') + 1, sample.Length - sample.LastIndexOf(':') - 1);
//last:stringsegment

Bu tip de yine ArrayString gibi slicingi desteklemekte. Bunun için StringSegment.SubSegment metodunu kullanabiliriz.

string sample = "test:0:stringsegment";
StringSegment last = new StringSegment(sample, sample.LastIndexOf(':') + 1, sample.Length - sample.LastIndexOf(':') - 1);
//last:stringsegment                   
StringSegment segment = last.Subsegment(6);
//segment:segment

Evet gördüğümüz üzere ArraySegment ve StringSegment, array ve string üzerindeki belirli alanlarda çalışabilmek için kullanabileceğimiz tiplerden bir tanesi. Bu tipleri kullanarak örneğin arrayin belirli bölgelerinde multithread olarak çalışma yapmamız mümkün. Aynı zamanda async metotlarda da kullanabiliyoruz. Span ve memory'den farkları nedir diye baktığımızda ise hatırlarsanız span ve memory birden fazla tipteki memory bölgelerinde çalışabiliyordu. String ve ArraySegment ise sadece array ve string üzerinde çalışabiliyor. Aynı zamanda bu tipler size orjinal kaynağa erişim ve o kaynağı değiştirme izni de veriyor. Yani siz ArraySegment üzerinden Array propertysine ulaşıp istediğiniz herhangi bir elemanı değiştirebilirsiniz. ReadonlySpan veya ReadonlyMemory kullanarak bunun önüne geçebilirsiniz.

Eğer sadece array veya string ile çalışacaksanız ve bu tipler üzerinde parsing veya bu tiplerin farklı bölümlerini multithread olarak işleme gibi işlemler yapacaksanız bu tipleri kullanabilirsiniz. Örneğin ASP.NET Core içerisinde headerların parsing işleminde(https://github.com/dotnet/aspnetcore/blob/master/src/Http/Headers/src/MediaTypeHeaderValue.cs) doğrudan StringSegment'in kullanıldığını görebilirsiniz. Böylece parsing esnasında hiçbir şekilde gereksiz string üretimi gerçekleştirilmiyor ve etkin bir implementasyon yapılıyor.

Bir sonraki yazıda görüşmek üzere