1. Giriş
Təsəvvür edin böyük bir data massivi var, məsələn fayldan oxunmuş baytlar. Onun bir hissəsini emal etmək istəyirsiniz — bir parçasını string kimi oxumaq və ya sadəcə məlumat parçasını metoda ötürmək. "Köhnə" stilə görə bu baytları yeni massiva kopyalayardınız — bu yavaşdır və yaddaş sərf edir. Bəs belə parçalar onlarla və ya yüzlərlədirsə?
Burada qəhrəman peyda olur — Span<T>. Bu, massiv (və ya istənilən ardıcıl yaddaş parçası) üzərində kopyalamadan dilimi (view) təsvir edən xüsusi strukturdur: sadəcə lazım olan sahəyə işarə edir. Vacibdir: Span<T> yeni kolleksiya deyil, mövcud massivə olan təhlükəsiz "pəncərə"dir!
Span<T> əsas xüsusiyyətləri və məhdudiyyətləri
- Span<T> — elementləri kopyalamayan yaddaşa baxışı təmsil edən struktur (value type).
- Yalnız ardıcıl yaddaş sahəsinə istinad edir: massiv, massivin bir hissəsi, stackalloc bloku, sətrin yaddaşı xüsusi metodlar vasitəsilə və ya unsafe kod daxilində olan yaddaş.
- Span<T> stack-də yaşayır. Onu class sahəsində saxlamaq və ya birbaşa async metodlardan qaytarmaq olmaz.
- Növ təhlükəsizliyini təmin edir: yaddaşa giriş — göstəricilərlə əl işinə ehtiyac olmadan (əgər unsafe istifadə etmirsinizsə).
C# 14-ün yeni imkanlarına qısa nəzər
C# 14 dilimlərlə işə əlavə rahatlıqlar gətirir: faydalı .. aralıqlar və sondan indekslər ^1, massivlər və dilimlər üçün yaxşılaşdırılmış pattern matching və digər "sintaksis şirniyyəti". Buna sonda yenidən toxunacağıq.
2. Span<T> necə yaratmaq olar?
Sadə massivdən başlayaq və span yaradaq:
int[] numbers = { 10, 20, 30, 40, 50, 60 };
Span<int> midSpan = new Span<int>(numbers, 2, 3); // 3-cü elementdən (indeks 2) 3 element götür
İndi midSpan 30, 40, 50 elementlərinə işarə edir — bu kopya deyil, massivdəki "canlı" elementlərə baxışdır. Onları span vasitəsilə dəyişdirsəniz, əsas massiv də dəyişir.
midSpan[0] = 100;
Console.WriteLine(numbers[2]); // 100 çap olunacaq
Niyə massiv dilimlərini birbaşa istifadə etməyəsiniz?
Klassik LINQ dilimi yeni massiv yaradır:
int[] slice = numbers.Skip(2).Take(3).ToArray(); // <-- burada kopya yaradılır!
Bu vaxt və yaddaşa bahadır. Span artıq kopyalama problemini həll edir — və yalnız ədədlərlə işləmir.
3. Span<T> yaratmağın daha çox yolu
Mövcud massiv
Span<int> mySpan = numbers; // Massivdən Span-a implicit konversiya
Massivin bir hissəsi
int[] numbers = {10, 20, 30, 40, 50, 60};
Span<int> part = numbers.AsSpan(1, 4); // indeks 1-dən 4 element: {20, 30, 40, 50}
Stack yaddaşı (stackalloc)
Span<T> stack-də massiv ayırmağa imkan verir:
Span<byte> buffer = stackalloc byte[128];
for (int i = 0; i < buffer.Length; i++)
buffer[i] = (byte)i;
Stack yaddaşı sürətlidir və metoda çıxanda avtomatik azad edilir. Amma həcm məqbul olmalıdır — megabaytlarla stack-da işləmək olmaz.
Sətrlər və ReadOnlySpan<char>
.NET-də sətrlər dəyişməzdir, ona görə ReadOnlySpan<char> istifadə edirik:
string greeting = "Salam, C# dünya!";
ReadOnlySpan<char> span = greeting.AsSpan(7, 8); // "C# dünya"
Console.WriteLine(span.ToString()); // C# dünya
4. Gəlin nümunə quraq!
using System;
class Program
{
static void Main()
{
int[] orderTotals = { 100, 200, 300, 400, 500, 600, 700 };
Console.WriteLine("Bütün sifariş tarixi: ");
foreach (int total in orderTotals)
Console.Write(total + " ");
Console.WriteLine();
Console.WriteLine("1-dən 3-ə (indekslər 1-3) sifarişləri göstər:");
Span<int> recentOrders = orderTotals.AsSpan(1, 3);
foreach (int t in recentOrders)
Console.Write(t + " ");
Console.WriteLine();
// Span vasitəsilə datanı dəyişirik
recentOrders[1] = 999;
Console.WriteLine("Span vasitəsilə dəyişdikdən sonra:");
foreach (int total in orderTotals)
Console.Write(total + " ");
Console.WriteLine();
}
}
recentOrders dilimi massivlə birbaşa işləyir — bu kopya deyil.
5. Təhlükəsizlik, performans və yoxlamalar
- Yaddaşa qənaətli yanaşma: əlavə kopyalar yoxdur.
- Sərhəd yoxlamaları massivdən çıxmanı əngəlləyir.
- JIT optimizasiyaları çox sürətli giriş təmin edir (heç bir unsafe olmadan).
Metodlarla qarşılıqlı əlaqə
Metodlar Span<T> və ya ReadOnlySpan<T> qəbul edə bilər. Məlumatı dəyişdirmək niyyətiniz yoxdursa — ReadOnlySpan<T> istifadə edin.
static int Sum(Span<int> slice)
{
int sum = 0;
foreach (var item in slice)
sum += item;
return sum;
}
int[] data = { 1, 2, 3, 4, 5, 6, 7 };
Console.WriteLine(Sum(data.AsSpan(2, 3))); // 3+4+5=12
6. Range (aralıq) istifadə
C# 8-dən etibarən .. aralıqlar və sondan indekslər ^ mövcuddur — onlar dilimlərlə gözəl işləyir.
int[] arr = { 10, 20, 30, 40, 50, 60 };
Span<int> span = arr[2..5]; // indekslər 2,3,4 - yəni 30, 40, 50
Aralıqda başlanğıc indeksi daxil edilir, son indeks daxil edilmir.
Sondan indekslər
int lastElement = arr[^1]; // son element (60)
Span<int> lastTwo = arr[^2..]; // son iki element (50, 60)
Aralıqlar və sətrlər
string code = "SpanSehir!";
var mid = code[4..9]; // "Sehir"
Bu dilim ReadOnlySpan<char> olacaq; string almaq üçün ToString() çağırın.
7. Müasir pattern-lər ilə dilimlərlə işləmə (C# 14)
Yeni pattern-lər massivlər və dilimləri parçalamağı sadələşdirir.
if (arr is [10, 20, .. var rest]) // .. massivdəki "qalan" hissəni tutur
{
Console.WriteLine("Başlanğıc uyğun gəldi, qalan hissə:");
foreach (var x in rest)
Console.WriteLine(x);
}
if (arr is [.., 50, 60])
Console.WriteLine("Massiv 50, 60 ilə bitir");
Qıyaslama: Massiv, ArraySegment və Span
| Tip | Mutable | Məlumatı kopyalayır? | Stack-da ola bilər | Class/struktur | Class sahəsi etmək olar |
|---|---|---|---|---|---|
|
bəli | - | xeyr | class | bəli |
|
bəli | xeyr | xeyr | struktur | bəli |
|
bəli | xeyr | struktur | xeyr | |
|
xeyr | xeyr | struktur | xeyr |
Başqa sözlə, Span<T> — ArraySegment<T>-in evolusiyasıdır: daha performanslı və təhlükəsiz.
8. Tipik səhvlər və tələfərlər
Span<T>-ı class sahəsində saxlamağa cəhd. Olmaz: sahələr heap-də yaşayır, amma Span<T> stack-də olmalıdır. Bunun əvəzinə ArraySegment<T> və ya indekslərdən istifadə edin.
Span<T>-ı async metodlardan qaytarmaq/saxlamaq. Olmaz: asinxronluq stack-ı qırır. Məlumatı başqa cür ötürün — məsələn, massiv, Memory<T>/ReadOnlyMemory<T>.
Dilim yaratarkən səhv aralıq. Sərhədləri aşmaq runtime zamanı exception verir. Həmişə uzunluğu və sərhədləri yoxlayın.
Span-ın əsas məlumatları əks etdirdiyini unutmaq. Span vasitəsilə hər hansı dəyişiklik əsas massivi dəyişdirir. Müstəqil kopya lazımdırsa — açıq şəkildə məlumatı kopyalayın.
GO TO FULL VERSION