CodeGym /Kurslar /C# SELF /Yeni imkanlar Span<T>...

Yeni imkanlar Span<T>

C# SELF
Səviyyə , Dərs
Mövcuddur

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
T[]
bəli - xeyr class bəli
ArraySegment<T>
bəli xeyr xeyr struktur bəli
Span<T>
bəli xeyr struktur xeyr
ReadOnlySpan<T>
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.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION