Bagikan melalui


Indeks dan rentang

Rentang dan indeks menyediakan sintaksis yang singkat untuk mengakses elemen atau rentang tunggal secara berurutan.

Dalam tutorial ini, Anda akan mempelajari cara:

  • Gunakan sintaks untuk rentang dalam sebuah urutan.
  • Secara implisit mendefinisikan Range.
  • Pahami keputusan desain untuk awal dan akhir setiap urutan.
  • Pelajari skenario untuk jenis Index dan Range.

Dukungan bahasa untuk indeks dan rentang

Indeks dan rentang menyediakan sintaksis yang singkat untuk mengakses elemen atau rentang tunggal secara berurutan.

Dukungan bahasa ini bergantung pada dua jenis baru dan dua operator baru:

Mari kita mulai dengan aturan untuk indeks. Pertimbangkan array sequence. Indeks 0 sama dengan sequence[0]. Indeks ^0 sama dengan sequence[sequence.Length]. Ekspresi sequence[^0] melemparkan pengecualian, seperti sequence[sequence.Length] halnya. Untuk angka napa pun , indeksnya ^n sama dengan sequence.Length - n.

private string[] words = [
                // index from start     index from end
    "first",    // 0                    ^10
    "second",   // 1                    ^9
    "third",    // 2                    ^8
    "fourth",   // 3                    ^7
    "fifth",    // 4                    ^6
    "sixth",    // 5                    ^5
    "seventh",  // 6                    ^4
    "eighth",   // 7                    ^3
    "ninth",    // 8                    ^2
    "tenth"     // 9                    ^1
];              // 10 (or words.Length) ^0

Anda dapat mengambil kata terakhir dengan indeks ^1. Tambahkan kode berikut di bawah inisialisasi:

Console.WriteLine($"The last word is < {words[^1]} >."); // The last word is < tenth >.

Rentang menentukan awal dan akhir rentang. Awal rentang bersifat inklusif, tetapi akhir rentang bersifat eksklusif, yang berarti awal disertakan dalam rentang tetapi akhir tidak disertakan dalam rentang. Rentang [0..^0] mewakili seluruh rentang, sama seperti [0..sequence.Length] mewakili seluruh rentang.

Kode berikut membuat subrange dengan kata "kedua", "ketiga", dan "keempat". Ini termasuk words[1] hingga words[3]. Elemen words[4] tidak berada dalam rentang.

string[] secondThirdFourth = words[1..4]; // contains "second", "third" and "fourth"

// < second >< third >< fourth >
foreach (var word in secondThirdFourth)
    Console.Write($"< {word} >"); 
Console.WriteLine();

Kode berikut mengembalikan rentang yang mencakup "kesembilan" dan "kesepuluh". Ini termasuk words[^2] dan words[^1]. Indeks words[^0] akhir tidak disertakan.

 string[] lastTwo = words[^2..^0]; // contains "ninth" and "tenth"

 // < ninth >< tenth >
 foreach (var word in lastTwo)
     Console.Write($"< {word} >"); 
 Console.WriteLine();

Contoh berikut membuat rentang yang tidak memiliki batas untuk awal, akhir, atau keduanya:

string[] allWords = words[..]; // contains "first" through "tenth".
string[] firstPhrase = words[..4]; // contains "first" through "fourth"
string[] lastPhrase = words[6..]; // contains "seventh", "eight", "ninth" and "tenth"

// < first >< second >< third >< fourth >< fifth >< sixth >< seventh >< eighth >< ninth >< tenth >
foreach (var word in allWords)
    Console.Write($"< {word} >"); 
Console.WriteLine();

// < first >< second >< third >< fourth >
foreach (var word in firstPhrase)
    Console.Write($"< {word} >"); 
Console.WriteLine();

// < seventh >< eighth >< ninth >< tenth >
foreach (var word in lastPhrase)
    Console.Write($"< {word} >"); 
Console.WriteLine();

Anda juga dapat mendeklarasikan rentang atau indeks sebagai variabel. Variabel kemudian dapat digunakan di dalam [ karakter dan ] :

Index thirdFromEnd = ^3;
Console.WriteLine($"< {words[thirdFromEnd]} > "); // < eighth > 
Range phrase = 1..4;
string[] text = words[phrase];

// < second >< third >< fourth >
foreach (var word in text)
    Console.Write($"< {word} >");  
Console.WriteLine();

Sampel berikut menunjukkan banyak alasan untuk pilihan tersebut. Ubah x, y, dan z untuk mencoba kombinasi yang berbeda. Saat Anda bereksperimen, gunakan nilai di mana x kurang dari y, dan y kurang dari z untuk kombinasi yang valid. Tambahkan kode berikut dalam metode baru. Coba kombinasi yang berbeda:

int[] numbers = [..Enumerable.Range(0, 100)];
int x = 12;
int y = 25;
int z = 36;

Console.WriteLine($"{numbers[^x]} is the same as {numbers[numbers.Length - x]}");
Console.WriteLine($"{numbers[x..y].Length} is the same as {y - x}");

Console.WriteLine("numbers[x..y] and numbers[y..z] are consecutive and disjoint:");
Span<int> x_y = numbers[x..y];
Span<int> y_z = numbers[y..z];
Console.WriteLine($"\tnumbers[x..y] is {x_y[0]} through {x_y[^1]}, numbers[y..z] is {y_z[0]} through {y_z[^1]}");

Console.WriteLine("numbers[x..^x] removes x elements at each end:");
Span<int> x_x = numbers[x..^x];
Console.WriteLine($"\tnumbers[x..^x] starts with {x_x[0]} and ends with {x_x[^1]}");

Console.WriteLine("numbers[..x] means numbers[0..x] and numbers[x..] means numbers[x..^0]");
Span<int> start_x = numbers[..x];
Span<int> zero_x = numbers[0..x];
Console.WriteLine($"\t{start_x[0]}..{start_x[^1]} is the same as {zero_x[0]}..{zero_x[^1]}");
Span<int> z_end = numbers[z..];
Span<int> z_zero = numbers[z..^0];
Console.WriteLine($"\t{z_end[0]}..{z_end[^1]} is the same as {z_zero[0]}..{z_zero[^1]}");

Tidak hanya array yang mendukung indeks dan rentang. Anda juga dapat menggunakan indeks dan rentang dengan string, , Span<T>atau ReadOnlySpan<T>.

Konversi ekspresi pengubah rentang yang implisit

Saat menggunakan sintaks ekspresi operator rentang, kompilator secara implisit mengonversi nilai awal dan akhir menjadi Index dan dari nilai-nilai tersebut, membuat instans baru Range. Kode berikut menunjukkan contoh konversi implisit dari sintaks ekspresi operator rentang, dan alternatif eksplisit yang sesuai:

Range implicitRange = 3..^5;

Range explicitRange = new(
    start: new Index(value: 3, fromEnd: false),
    end: new Index(value: 5, fromEnd: true));

if (implicitRange.Equals(explicitRange))
{
    Console.WriteLine(
        $"The implicit range '{implicitRange}' equals the explicit range '{explicitRange}'");
}
// Sample output:
//     The implicit range '3..^5' equals the explicit range '3..^5'

Penting

Konversi implisit dari Int32 ke Index melempar ArgumentOutOfRangeException ketika nilainya negatif. Demikian juga, konstruktor Index melempar ArgumentOutOfRangeException ketika parameter value negatif.

Dukungan tipe untuk indeks dan rentang

Indeks dan rentang memberikan sintaks yang jelas dan ringkas untuk mengakses satu elemen atau rentang elemen secara berurutan. Ekspresi indeks biasanya mengembalikan jenis elemen urutan. Ekspresi rentang biasanya mengembalikan jenis urutan yang sama dengan urutan sumber.

Jenis apa pun yang menyediakan pengindeks dengan Index parameter atau Range secara eksplisit mendukung indeks atau rentang masing-masing. Pengindeks yang mengambil satu Range parameter dapat mengembalikan jenis urutan yang berbeda, seperti System.Span<T>.

Penting

Performa kode menggunakan operator rentang tergantung pada jenis operand urutan.

Kompleksitas waktu operator rentang tergantung pada jenis urutan. Misalnya, jika urutannya adalah string array atau , maka hasilnya adalah salinan dari bagian input yang ditentukan, sehingga kompleksitas waktu adalah O(N) (di mana N adalah panjang rentang). Di sisi lain, jika itu adalah System.Span<T> atau System.Memory<T>, hasilnya mereferensikan penyimpanan backing yang sama, yang berarti tidak ada salinan dan operasinya adalah O(1).

Selain kompleksitas waktu, ini juga menyebabkan alokasi dan penyalinan tambahan yang mempengaruhi kinerja. Dalam kode sensitif performa, pertimbangkan untuk menggunakan Span<T> atau Memory<T> sebagai jenis urutan, karena operator rentang tidak mengalokasikannya.

Jenis dapat dihitung jika memiliki properti bernama Length atau Count dengan getter yang dapat diakses dan tipe pengembalian adalah int. Jenis yang dapat dihitung yang tidak secara eksplisit mendukung indeks atau rentang dapat memberikan dukungan implisit untuk mereka. Untuk informasi selengkapnya, lihat bagian dukungan Indeks Implisit dan dukungan Rentang Implisit dari catatan proposal fitur. Rentang yang menggunakan dukungan rentang implisit mengembalikan jenis urutan yang sama dengan urutan sumber.

Misalnya, jenis .NET berikut mendukung indeks dan rentang: String, , Span<T>dan ReadOnlySpan<T>. List<T> mendukung indeks tetapi tidak mendukung rentang.

Array memiliki perilaku yang lebih bernuansa. Array satu dimensi mendukung baik indeks maupun rentang. Array multi-dimensi tidak mendukung pengindeks atau rentang. Pengindeks untuk array multi-dimensi memiliki beberapa parameter, bukan parameter tunggal. Array jagged, juga disebut sebagai array, mendukung rentang dan pengindeks. Contoh berikut menunjukkan cara melakukan iterasi subbagian persegi panjang tertentu dari array tak beraturan. Ini berulang bagian di tengah, tidak termasuk tiga baris pertama dan terakhir, dan dua kolom pertama dan terakhir dari setiap baris yang dipilih:

int[][] jagged = 
[
   [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
   [10,11,12,13,14,15,16,17,18,19],
   [20,21,22,23,24,25,26,27,28,29],
   [30,31,32,33,34,35,36,37,38,39],
   [40,41,42,43,44,45,46,47,48,49],
   [50,51,52,53,54,55,56,57,58,59],
   [60,61,62,63,64,65,66,67,68,69],
   [70,71,72,73,74,75,76,77,78,79],
   [80,81,82,83,84,85,86,87,88,89],
   [90,91,92,93,94,95,96,97,98,99],
];

var selectedRows = jagged[3..^3];

foreach (var row in selectedRows)
{
    var selectedColumns = row[2..^2];
    foreach (var cell in selectedColumns)
    {
        Console.Write($"{cell}, ");
    }
    Console.WriteLine();
}

Dalam semua kasus, operator rentang untuk Array mengalokasikan array untuk menyimpan elemen yang dikembalikan.

Skenario untuk indeks dan rentang

Anda akan sering menggunakan rentang dan indeks saat ingin menganalisis sebagian dari urutan yang lebih besar. Sintaks baru lebih jelas dalam membaca bagian urutan apa yang terlibat. Fungsi MovingAverage lokal mengambil Range sebagai argumennya. Metode ini kemudian menghitung hanya rentang itu saat menghitung min, maks, dan rata-rata. Coba kode berikut dalam proyek Anda:

int[] sequence = Sequence(1000);

for(int start = 0; start < sequence.Length; start += 100)
{
    Range r = start..(start+10);
    var (min, max, average) = MovingAverage(sequence, r);
    Console.WriteLine($"From {r.Start} to {r.End}:    \tMin: {min},\tMax: {max},\tAverage: {average}");
}

for (int start = 0; start < sequence.Length; start += 100)
{
    Range r = ^(start + 10)..^start;
    var (min, max, average) = MovingAverage(sequence, r);
    Console.WriteLine($"From {r.Start} to {r.End}:  \tMin: {min},\tMax: {max},\tAverage: {average}");
}

(int min, int max, double average) MovingAverage(int[] subSequence, Range range) =>
    (
        subSequence[range].Min(),
        subSequence[range].Max(),
        subSequence[range].Average()
    );

int[] Sequence(int count) => [..Enumerable.Range(0, count).Select(x => (int)(Math.Sqrt(x) * 100))];

Catatan tentang Indeks Rentang dan Array

Saat mengambil rentang dari array, hasilnya adalah array yang disalin dari array awal, bukan direferensikan. Mengubah nilai dalam array yang dihasilkan tidak akan mengubah nilai dalam array awal.

Contohnya:

var arrayOfFiveItems = new[] { 1, 2, 3, 4, 5 };

var firstThreeItems = arrayOfFiveItems[..3]; // contains 1,2,3
firstThreeItems[0] =  11; // now contains 11,2,3

Console.WriteLine(string.Join(",", firstThreeItems));
Console.WriteLine(string.Join(",", arrayOfFiveItems));

// output:
// 11,2,3
// 1,2,3,4,5

Lihat juga