Повний посібник із прикладами та практичними завданнями
Індекси
Діапазони
Індексатори
Практика
Пісочниця
Індекси в C# 8.0+
Індекси дозволяють звертатися до елементів колекції з кінця, використовуючи оператор ^ (карет).
Основні поняття:
^1 - останній елемент
^2 - передостанній елемент
^0 - межа масиву (еквівалентно array.Length)
^ працює тільки з колекціями, що мають властивість Length або Count
Основи використання індексів
using System;
class Program {
static void Main() {
int[] numbers = { 10, 20, 30, 40, 50 };
// Доступ до елементів з початку
Console.WriteLine($"numbers[0] = {numbers[0]}"); // 10
Console.WriteLine($"numbers[1] = {numbers[1]}"); // 20
// Доступ до елементів з кінця (новий синтаксис)
Console.WriteLine($"numbers[^1] = {numbers[^1]}"); // 50 (останній)
Console.WriteLine($"numbers[^2] = {numbers[^2]}"); // 40 (передостанній)
Console.WriteLine($"numbers[^3] = {numbers[^3]}"); // 30
// Рядки теж підтримують індекси
string text = "Hello, C#!";
Console.WriteLine($"text[^1] = '{text[^1]}'"); // '!'
Console.WriteLine($"text[^3] = '{text[^3]}'"); // 'C'
// Список
var list = new List<string> { "A", "B", "C", "D" };
Console.WriteLine($"list[^1] = {list[^1]}"); // "D"
}
}
Як працюють індекси?
Вираз array[^n] перетворюється компілятором на array[array.Length - n].
Приклад перетворення індексів
int[] arr = { 1, 2, 3, 4, 5 };
// Ці вирази еквівалентні:
int last1 = arr[^1]; // Синтаксис C# 8.0+
int last2 = arr[arr.Length - 1]; // Традиційний синтаксис
// І так теж:
int secondLast1 = arr[^2];
int secondLast2 = arr[arr.Length - 2];
Console.WriteLine($"arr[^1] = {last1}, arr[arr.Length-1] = {last2}");
Console.WriteLine($"arr[^2] = {secondLast1}, arr[arr.Length-2] = {secondLast2}");
Важливі моменти:
^0 викличе виняток при прямому доступі
Індекси працюють з масивами, рядками, Span, списками та іншими колекціями
Можна створювати свої типи, що підтримують індекси
Демонстрація індексів
Діапазони в C# 8.0+
Діапазони дозволяють отримувати зрізи (слайси) масивів та інших колекцій за допомогою оператора ...
Синтаксис діапазонів:
array[..] - весь масив (копія)
array[1..] - з елемента з індексом 1 до кінця
array[..^1] - всі елементи крім останнього
array[1..4] - елементи з індексами 1, 2, 3
array[^3..^1] - останні 3 елементи (крім найостаннішого)
Приклади використання діапазонів
using System;
class Program {
static void Main() {
int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// Повний діапазон (копія масиву)
int[] all = numbers[..];
Console.WriteLine($"all: {string.Join(", ", all)}");
// Від початку до 4-го елемента (не включаючи)
int[] first4 = numbers[..4];
Console.WriteLine($"first4: {string.Join(", ", first4)}");
// Від 2-го елемента до кінця
int[] from2 = numbers[2..];
Console.WriteLine($"from2: {string.Join(", ", from2)}");
// З 2-го до 6-го елемента
int[] slice2to6 = numbers[2..6];
Console.WriteLine($"slice2to6: {string.Join(", ", slice2to6)}");
// Останні 3 елементи
int[] last3 = numbers[^3..];
Console.WriteLine($"last3: {string.Join(", ", last3)}");
// Всі крім першого та останнього
int[] middle = numbers[1..^1];
Console.WriteLine($"middle: {string.Join(", ", middle)}");
// З передостаннього до передпередостаннього
int[] tricky = numbers[^3..^1];
Console.WriteLine($"tricky: {string.Join(", ", tricky)}");
// З рядками
string text = "Hello, World!";
string hello = text[..5]; // "Hello"
string world = text[7..^1]; // "World"
Console.WriteLine($"hello = '{hello}', world = '{world}'");
}
}
Тип Range
Оператор .. створює значення типу Range.
Робота з типом Range явно
using System;
class Program {
static void Main() {
int[] numbers = { 0, 1, 2, 3, 4, 5 };
// Явне створення діапазонів
Range firstHalf = 0..3;
Range lastTwo = ^2..;
Range middleRange = 1..^1;
// Використання діапазонів
var slice1 = numbers[firstHalf]; // [0, 1, 2]
var slice2 = numbers[lastTwo]; // [4, 5]
var slice3 = numbers[middleRange]; // [1, 2, 3, 4]
// Перевірка меж діапазону
var range = 2..5;
Console.WriteLine($"Start: {range.Start}, End: {range.End}");
Console.WriteLine($"Start value: {range.Start.Value}, " +
$"IsFromEnd: {range.Start.IsFromEnd}");
// Діапазон за замовчуванням - весь масив
Range all = ..;
Console.WriteLine($"All range: {string.Join(", ", numbers[all])}");
}
}
Порівняння синтаксису
Новий синтаксис
Старий синтаксис
Опис
arr[..]
arr[0..arr.Length]
Весь масив
arr[1..]
arr[1..arr.Length]
З другого елемента
arr[..^1]
arr[0..(arr.Length-1)]
Всі крім останнього
arr[^3..]
arr[(arr.Length-3)..arr.Length]
Останні три
Демонстрація діапазонів
Індексатори в C#
Індексатори дозволяють звертатися до об'єктів як до масивів, використовуючи синтаксис об'єкт[індекс].
Коли використовувати індексатори:
Класи, які представляють колекції
Об'єкти з внутрішнім масивом або списком
Кастомні структури даних
Обгортки над словниками або іншими колекціями
Базовий приклад індексатора
using System;
// Клас із простим індексатором
class StringCollection {
private string[] items = new string[10];
// Індексатор для доступу за цілочисельним індексом
public string this[int index] {
get {
if (index < 0 || index >= items.Length)
throw new IndexOutOfRangeException();
return items[index];
}
set {
if (index < 0 || index >= items.Length)
throw new IndexOutOfRangeException();
items[index] = value;
}
}
public int Count => items.Length;
}
class Program {
static void Main() {
var collection = new StringCollection();
// Використовуємо індексатор як масив
collection[0] = "Hello";
collection[1] = "World";
collection[2] = "C#";
Console.WriteLine($"collection[0] = {collection[0]}");
Console.WriteLine($"collection[1] = {collection[1]}");
Console.WriteLine($"collection[2] = {collection[2]}");
// Перебір через індексатор
for (int i = 0; i < 3; i++) {
Console.WriteLine($"collection[{i}] = {collection[i]}");
}
}
}
Багатовимірні індексатори
Матриця з двовимірним індексатором
using System;
class Matrix {
private double[,] data;
private int rows, cols;
public Matrix(int rows, int cols) {
this.rows = rows;
this.cols = cols;
data = new double[rows, cols];
}
// Двовимірний індексатор
public double this[int row, int col] {
get {
ValidateIndex(row, col);
return data[row, col];
}
set {
ValidateIndex(row, col);
data[row, col] = value;
}
}
private void ValidateIndex(int row, int col) {
if (row < 0 || row >= rows || col < 0 || col >= cols)
throw new IndexOutOfRangeException(
$"Індекс [{row},{col}] поза межами [{rows},{cols}]");
}
public void Print() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
Console.Write($"{this[i, j]:F2}\t");
}
Console.WriteLine();
}
}
}
class Program {
static void Main() {
var matrix = new Matrix(3, 3);
// Заповнюємо матрицю через індексатор
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
matrix[i, j] = i * 10 + j + 1;
}
}
Console.WriteLine("Матриця 3x3:");
matrix.Print();
// Доступ до окремих елементів
Console.WriteLine($"\nmatrix[1, 2] = {matrix[1, 2]}");
Console.WriteLine($"matrix[2, 0] = {matrix[2, 0]}");
}
}
Індексатори з користувацькими типами індексів
Підтримка індексів та діапазонів
using System;
class SmartArray<T> {
private T[] data;
public SmartArray(params T[] items) {
data = items;
}
// Звичайний індексатор
public T this[int index] {
get => data[index];
set => data[index] = value;
}
// Індексатор з підтримкою Index (для ^)
public T this[Index index] {
get => data[index];
set => data[index] = value;
}
// Індексатор з підтримкою Range (для ..)
public T[] this[Range range] {
get {
var (offset, length) = range.GetOffsetAndLength(data.Length);
var result = new T[length];
Array.Copy(data, offset, result, 0, length);
return result;
}
}
public void Print() {
Console.WriteLine($"[{string.Join(", ", data)}]");
}
}
class Program {
static void Main() {
var arr = new SmartArray<int>(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
Console.Write("Вихідний масив: ");
arr.Print();
// Використовуємо новий синтаксис індексів
Console.WriteLine($"arr[^1] = {arr[^1]}"); // Останній
Console.WriteLine($"arr[^3] = {arr[^3]}"); // Передпередостанній
// Використовуємо діапазони
Console.Write("\narr[2..5] = ");
var slice1 = arr[2..5];
Console.WriteLine($"[{string.Join(", ", slice1)}]");
Console.Write("arr[^4..^1] = ");
var slice2 = arr[^4..^1];
Console.WriteLine($"[{string.Join(", ", slice2)}]");
Console.Write("arr[..^2] = ");
var slice3 = arr[..^2];
Console.WriteLine($"[{string.Join(", ", slice3)}]");
}
}
Кращі практики:
Завжди перевіряйте межі індексів
Документуйте поведінку індексаторів
Розгляньте підтримку Index та Range у своїх колекціях
Не зловживайте індексаторами - використовуйте їх там, де це логічно
Створення кастомного індексатора
Практичні завдання
Розв'яжіть ці задачі для закріплення матеріалу. Натисніть на задачу, щоб побачити рішення.
Завдання 1: Зворотний доступ до масиву
Створіть метод, який повертає масив у зворотному порядку, використовуючи індекси з кінця.
public static T[] ReverseArray<T>(T[] array) {
T[] result = new T[array.Length];
for (int i = 0; i < array.Length; i++) {
result[i] = array[^(i + 1)]; // Використовуємо індекс з кінця
}
return result;
}
// Використання:
int[] numbers = { 1, 2, 3, 4, 5 };
var reversed = ReverseArray(numbers);
// reversed = [5, 4, 3, 2, 1]
Завдання 2: Видалити перший та останній елемент
Використовуючи діапазони, створіть метод, який повертає масив без першого та останнього елемента.
public static T[] TrimArray<T>(T[] array) {
if (array.Length <= 2)
return Array.Empty<T>();
return array[1..^1]; // Всі крім першого та останнього
}
// Використання:
string[] words = { "first", "second", "third", "fourth", "last" };
var trimmed = TrimArray(words);
// trimmed = ["second", "third", "fourth"]
Завдання 3: Клас CircularBuffer з індексатором
Створіть клас кільцевого буфера з індексатором, який підтримує доступ за індексом з кінця.
public class CircularBuffer<T> {
private T[] buffer;
private int start;
public CircularBuffer(int capacity) {
buffer = new T[capacity];
start = 0;
}
public int Count { get; private set; }
// Індексатор з підтримкою Index
public T this[Index index] {
get {
int i = index.GetOffset(Count);
if (i < 0 || i >= Count)
throw new IndexOutOfRangeException();
return buffer[(start + i) % buffer.Length];
}
set {
int i = index.GetOffset(Count);
if (i < 0 || i >= Count)
throw new IndexOutOfRangeException();
buffer[(start + i) % buffer.Length] = value;
}
}
public void Add(T item) {
buffer[(start + Count) % buffer.Length] = item;
if (Count < buffer.Length) {
Count++;
} else {
start = (start + 1) % buffer.Length;
}
}
}
// Використання:
var buffer = new CircularBuffer<int>(5);
for (int i = 1; i <= 7; i++) buffer.Add(i);
// buffer містить [3, 4, 5, 6, 7]
Console.WriteLine($"Останній елемент: {buffer[^1]}"); // 7
Console.WriteLine($"Перший елемент: {buffer[0]}"); // 3
Завдання 4: Розбиття рядка на частини
Використовуючи діапазони, розбийте рядок на рівні частини заданого розміру.
public static string[] SplitIntoChunks(string text, int chunkSize) {
if (chunkSize <= 0)
throw new ArgumentException("Розмір частини має бути додатнім");
int chunks = (int)Math.Ceiling(text.Length / (double)chunkSize);
string[] result = new string[chunks];
for (int i = 0; i < chunks; i++) {
int start = i * chunkSize;
int end = Math.Min(start + chunkSize, text.Length);
result[i] = text[start..end]; // Використовуємо діапазон
}
return result;
}
// Використання:
string text = "HelloWorldCSharp";
var chunks = SplitIntoChunks(text, 5);
// chunks = ["Hello", "World", "CShar", "p"]
Завдання 5: Кастомна колекція з діапазонами
Створіть колекцію, яка підтримує індекси, діапазони та має індексатор.
public class CustomCollection<T> {
private List<T> items = new List<T>();
public void Add(T item) => items.Add(item);
public int Count => items.Count;
// Звичайний індексатор
public T this[int index] {
get => items[index];
set => items[index] = value;
}
// Індексатор з Index
public T this[Index index] {
get => items[index];
set => items[index] = value;
}
// Індексатор з Range
public List<T> this[Range range] {
get {
var (offset, length) = range.GetOffsetAndLength(items.Count);
return items.GetRange(offset, length);
}
}
public override string ToString() {
return $"[{string.Join(", ", items)}]";
}
}
// Використання:
var collection = new CustomCollection<int>();
for (int i = 0; i < 10; i++) collection.Add(i * 10);
Console.WriteLine($"collection[^1] = {collection[^1]}"); // 90
Console.WriteLine($"collection[1..4] = [{string.Join(", ", collection[1..4])}]");
Пісочниця для експериментів
Напишіть свій код і запустіть його прямо в браузері!