Bagikan melalui


Operator dan ekspresi akses anggota - operator titik, pengindeks, dan pemanggilan.

Anda menggunakan beberapa operator dan ekspresi untuk mengakses elemen tipe. Operator akses anggota termasuk akses anggota (.), elemen array, atau akses pengindeks ([]), index-from-end (^), rentang (..), operator bersyarat-null (?. dan ?[]), dan pemanggilan metode (()). Operator ini termasuk operator akses anggota null-conditional (?.), dan akses pengindeks (?[]).

Ekspresi akses anggota .

Anda menggunakan token . untuk mengakses anggota dari ruang nama atau tipe, seperti yang ditunjukkan oleh contoh-contoh berikut:

  • Gunakan . untuk mengakses namespace bersarang dalam sebuah namespace, seperti yang ditunjukkan contoh direktif using berikut:
using System.Collections.Generic;
  • Gunakan . untuk membentuk nama yang memenuhi syarat untuk mengakses jenis dalam namespace, seperti yang ditunjukkan oleh kode berikut.
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];

Gunakan using direktiva untuk membuat penggunaan nama yang memenuhi syarat menjadi opsional.

  • Gunakan . untuk mengakses anggota jenis, statis dan nonstatis, seperti yang ditunjukkan oleh kode berikut:
List<double> constants =
[
    Math.PI,
    Math.E
];
Console.WriteLine($"{constants.Count} values to show:");
Console.WriteLine(string.Join(", ", constants));
// Output:
// 2 values to show:
// 3.14159265358979, 2.71828182845905

Anda juga dapat menggunakan . untuk mengakses metode ekstensi.

Operator pengindeks []

Kurung siku, [], biasanya digunakan untuk mengakses elemen array, pengindeks, atau penunjuk. Dimulai dengan C# 12, [] mengapit ekspresi koleksi.

Mengakses array

Contoh berikut menunjukkan cara mengakses elemen array:

int[] fib = new int[10];
fib[0] = fib[1] = 1;
for (int i = 2; i < fib.Length; i++)
{
    fib[i] = fib[i - 1] + fib[i - 2];
}
Console.WriteLine(fib[fib.Length - 1]);  // output: 55

double[,] matrix = new double[2,2];
matrix[0,0] = 1.0;
matrix[0,1] = 2.0;
matrix[1,0] = matrix[1,1] = 3.0;
var determinant = matrix[0,0] * matrix[1,1] - matrix[1,0] * matrix[0,1];
Console.WriteLine(determinant);  // output: -3

Jika indeks array berada di luar batas dimensi array yang sesuai, maka IndexOutOfRangeException akan dilemparkan.

Seperti yang ditunjukkan contoh sebelumnya, Anda juga menggunakan kurung siku saat mendeklarasikan jenis array atau untuk instansiasi instans array.

Untuk informasi selengkapnya tentang Arrays, lihat Arrays.

Mengakses pengindeks

Contoh berikut menggunakan jenis Dictionary<TKey,TValue> .NET untuk menunjukkan akses pengindeks:

var dict = new Dictionary<string, double>();
dict["one"] = 1;
dict["pi"] = Math.PI;
Console.WriteLine(dict["one"] + dict["pi"]);  // output: 4.14159265358979

Pengindeks memungkinkan Anda mengindeks instans jenis yang ditentukan pengguna dengan cara yang sama seperti pengindeksan array. Tidak seperti indeks array, yang harus berupa bilangan bulat, parameter pengindeks dapat dideklarasikan berdasarkan jenis apa pun.

Untuk informasi selengkapnya tentang pengindeks, lihat Pengindeks.

Penggunaan lainnya dari []

Untuk informasi tentang akses elemen penunjuk, lihat bagian Operator akses elemen Penunjuk [] dari artikel Operator terkait Penunjuk. Untuk informasi tentang ekspresi koleksi, lihat artikel ekspresi koleksi.

Anda juga menggunakan kurung siku untuk menentukan atribut:

[System.Diagnostics.Conditional("DEBUG")]
void TraceMethod() {}

Selain itu, kurung siku dapat digunakan untuk menunjuk pola daftar untuk digunakan dalam pencocokan pola atau pengujian.

arr is ([1, 2, ..])
//Specifies that an array starts with (1, 2)

Operator ?. null-conditional dan ?[]

Operator null-conditional hanya menerapkan operasi akses anggota (?.) atau akses elemen (?[]) pada operand-nya jika operand tersebut bernilai bukan null; jika tidak, akan mengembalikan null. Dengan kata lain:

  • Jika mengevaluasi menjadi , maka hasil dari atau adalah .

  • Jika a mengevaluasi ke bukan null, hasil dari a?.x atau a?[x] sama dengan hasil dari a.x atau a[x], berturut-turut.

    Catatan

    Jika a.x atau a[x] melempar pengecualian, a?.x atau a?[x] akan melempar pengecualian yang sama jika a tidak null. Misalnya, jika a adalah instance array yang bukan null dan x berada di luar batas a, a?[x] akan melempar IndexOutOfRangeException.

Operator null-kondisional memiliki sirkuit pendek. Artinya, jika satu operasi dalam rantai operasi pengaksesan anggota atau elemen bersyarat mengembalikan null, sisa rantai tidak dieksekusi. Dalam contoh berikut, B tidak dievaluasi jika A dievaluasi ke null dan C tidak dievaluasi jika A atau B dievaluasi ke null:

A?.B?.Do(C);
A?.B?[C];

Jika A mungkin null tetapi B dan C tidak akan null jika A bukan null, Anda hanya perlu menerapkan operator null-conditional ke A.

A?.B.C();

Dalam contoh sebelumnya, B tidak dievaluasi dan C() tidak dipanggil jika A null. Namun, jika akses anggota berantai terganggu, misalnya oleh tanda kurung seperti dalam (A?.B).C(), sirkuit pendek tidak terjadi.

Contoh berikut menunjukkan penggunaan operator ?. dan ?[]:

double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
{
    return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
}

var sum1 = SumNumbers(null, 0);
Console.WriteLine(sum1);  // output: NaN

List<double[]?> numberSets =
[
    [1.0, 2.0, 3.0],
    null
];

var sum2 = SumNumbers(numberSets, 0);
Console.WriteLine(sum2);  // output: 6

var sum3 = SumNumbers(numberSets, 1);
Console.WriteLine(sum3);  // output: NaN
namespace MemberAccessOperators2;

public static class NullConditionalShortCircuiting
{
    public static void Main()
    {
        Person? person = null;
        person?.Name.Write(); // no output: Write() is not called due to short-circuit.
        try
        {
            (person?.Name).Write();
        }
        catch (NullReferenceException)
        {
            Console.WriteLine("NullReferenceException");
        }; // output: NullReferenceException
    }
}

public class Person
{
    public required FullName Name { get; set; }
}

public class FullName
{
    public required string FirstName { get; set; }
    public required string LastName { get; set; }
    public void Write() => Console.WriteLine($"{FirstName} {LastName}");
}

Contoh pertama sebelumnya juga menggunakan operator koalesens-null (??) untuk menentukan ekspresi alternatif yang dievaluasi apabila hasil dari operasi bersyarat-null adalah null.

Jika a.x atau a[x] berjenis T tipe nilai yang tidak bisa null, maka a?.x atau a?[x] berjenis tipe nilai nullable yang sesuai T?. Jika Anda memerlukan ekspresi tipe T, terapkan operator penggabungan null ?? pada ekspresi null-kondisional, seperti yang ditunjukkan oleh contoh berikut:

int GetSumOfFirstTwoOrDefault(int[]? numbers)
{
    if ((numbers?.Length ?? 0) < 2)
    {
        return 0;
    }
    return numbers[0] + numbers[1];
}

Console.WriteLine(GetSumOfFirstTwoOrDefault(null));  // output: 0
Console.WriteLine(GetSumOfFirstTwoOrDefault([]));  // output: 0
Console.WriteLine(GetSumOfFirstTwoOrDefault([3, 4, 5]));  // output: 7

Dalam contoh sebelumnya, jika Anda tidak menggunakan operator ??, numbers?.Length < 2 mengevaluasi menjadi false saat numbers adalah null.

Catatan

Operator ?. mengevaluasi operand sebelah kirinya tidak lebih dari sekali, menjamin bahwa itu tidak dapat diubah menjadi null setelah diverifikasi sebagai non-null.

Dimulai dari C# 14, penugasan diperbolehkan dengan ekspresi akses bersyarat null (?. dan ?[]) pada jenis referensi. Misalnya, lihat metode berikut:

person?.FirstName = "Scott";
messages?[5] = "five";

Contoh sebelumnya menunjukkan penetapan ke properti dan elemen terindeks pada jenis referensi yang mungkin null. Perilaku penting untuk tugas ini adalah bahwa ekspresi di sisi kanan = dievaluasi hanya ketika sisi kiri diketahui tidak bernilai null. Misalnya, dalam kode berikut, fungsi GenerateNextIndex hanya dipanggil saat values array tidak null. Jika values array null, GenerateNextIndex tidak dipanggil:

person?.FirstName = "Scott";
messages?[5] = "five";

Dengan kata lain, kode sebelumnya setara dengan kode berikut menggunakan if pernyataan untuk pemeriksaan null:

if (values is not null)
{
    values[2] = GenerateNextIndex();
}

Selain penugasan, segala bentuk penugasan gabungan, seperti += atau -=, diizinkan. Namun, kenaikan (++) dan keputusan (--) tidak diizinkan.

Peningkatan ini tidak mengklasifikasikan ekspresi kondisional null sebagai variabel. Ini tidak dapat diassign ref, juga tidak dapat dipasangkan ke ref variabel atau diteruskan ke metode sebagai ref argumen atau out.

Pemanggilan delegasi aman utas

Gunakan operator ?. untuk memeriksa apakah delegasi merupakan bukan null dan panggil dengan cara utas-aman (misalnya, saat Anda menaikkan peristiwa), seperti yang ditunjukkan oleh kode berikut:

PropertyChanged?.Invoke(…)

Kode tersebut setara dengan kode berikut:

var handler = this.PropertyChanged;
if (handler != null)
{
    handler(…);
}

Contoh sebelumnya adalah cara aman dari gangguan antar utas untuk memastikan bahwa hanya handler yang tidak kosong yang dipanggil. Karena instans delegasi tidak dapat diubah, tidak ada utas yang dapat mengubah objek yang dirujuk oleh variabel lokal handler. Secara khusus, jika kode yang dijalankan oleh utas lain berhenti berlangganan dari peristiwa PropertyChanged dan PropertyChanged menjadi null sebelum handler dipanggil, objek yang dirujuk oleh handler tetap tidak terpengaruh.

Ekspresi pemanggilan ()

Gunakan tanda kurung, (), untuk memanggil metode atau memanggil delegasi.

Kode berikut menunjukkan cara memanggil metode, dengan atau tanpa argumen, dan memanggil delegasi:

Action<int> display = s => Console.WriteLine(s);

List<int> numbers =
[
    10,
    17
];
display(numbers.Count);   // output: 2

numbers.Clear();
display(numbers.Count);   // output: 0

Anda juga menggunakan tanda kurung saat memanggil konstruktor dengan operator baru.

Penggunaan lainnya dari ()

Anda juga menggunakan tanda kurung untuk menyesuaikan urutan untuk melakukan evaluasi operasi dalam ekspresi. Untuk informasi selengkapnya, lihat Operator C#.

Ekspresi cast, yang melakukan konversi tipe eksplisit, juga menggunakan kurung.

Indeks dari operator akhir ^

Operator indeks dan rentang dapat digunakan dengan jenis yang dapat dihitung. Jenis yang dapat dihitung adalah jenis yang memiliki properti bernama Count atau Length dengan aksesor get yang dapat diakses. Ekspresi koleksi juga mengandalkan tipe yang dapat dihitung.

Catatan

Array dimensi tunggal dapat dihitung. Array multi-dimensi bukanlah seperti yang dipikirkan. Operator ^ dan .. (rentang) tidak dapat digunakan dalam array multi-dimensi.

Operator ^ menunjukkan posisi elemen dari akhir sebuah deret. Untuk urutan sepanjang length, ^n menunjuk ke elemen dengan offset length - n dari awal urutan. Misalnya, ^1 merujuk ke elemen terakhir dari urutan dan ^length merujuk ke elemen pertama dari urutan.

int[] xs = [0, 10, 20, 30, 40];
int last = xs[^1];
Console.WriteLine(last);  // output: 40

List<string> lines = ["one", "two", "three", "four"];
string prelast = lines[^2];
Console.WriteLine(prelast);  // output: three

string word = "Twenty";
Index toFirst = ^word.Length;
char first = word[toFirst];
Console.WriteLine(first);  // output: T

Seperti yang ditunjukkan oleh contoh sebelumnya, ekspresi ^e adalah berjenis System.Index. Dalam ekspresi ^e, hasil dari e harus secara implisit dapat dikonversi ke int.

Anda juga dapat menggunakan operator ^ dengan operator rentang untuk membuat berbagai indeks. Untuk informasi selengkapnya, lihat Indeks dan rentang.

Dimulai dengan C# 13, Indeks dari operator akhir dapat digunakan dalam penginisialisasi objek.

Operator rentang ..

Operator .. menentukan awal dan akhir rentang indeks sebagai operandnya. Operand di sisi kiri merupakan awal dari rentang yang inklusif. Operand kanan adalah akhir rentang yang eksklusif. Salah satu operand dapat menjadi indeks dari awal atau dari akhir urutan, seperti yang ditunjukkan contoh berikut:

int[] numbers = [0, 10, 20, 30, 40, 50];
int start = 1;
int amountToTake = 3;
int[] subset = numbers[start..(start + amountToTake)];
Display(subset);  // output: 10 20 30

int margin = 1;
int[] inner = numbers[margin..^margin];
Display(inner);  // output: 10 20 30 40

string line = "one two three";
int amountToTakeFromEnd = 5;
Range endIndices = ^amountToTakeFromEnd..^0;
string end = line[endIndices];
Console.WriteLine(end);  // output: three

void Display<T>(IEnumerable<T> xs) => Console.WriteLine(string.Join(" ", xs));

Seperti yang ditunjukkan oleh contoh sebelumnya, ekspresi a..b adalah berjenis System.Range. Dalam ekspresi a..b, hasil dari a dan b harus secara implisit dapat dikonversi ke Int32 atau Index.

Penting

Konversi implisit dari int ke Index melempar ArgumentOutOfRangeException ketika nilainya negatif.

Anda dapat menghilangkan sembarang operand dari operator .. untuk mendapatkan rentang tak berujung:

  • a.. setara dengan a..^0
  • a.. setara dengan a..^0
  • a.. setara dengan a..^0
int[] numbers = [0, 10, 20, 30, 40, 50];
int amountToDrop = numbers.Length / 2;

int[] rightHalf = numbers[amountToDrop..];
Display(rightHalf);  // output: 30 40 50

int[] leftHalf = numbers[..^amountToDrop];
Display(leftHalf);  // output: 0 10 20

int[] all = numbers[..];
Display(all);  // output: 0 10 20 30 40 50

void Display<T>(IEnumerable<T> xs) => Console.WriteLine(string.Join(" ", xs));

Tabel berikut ini memperlihatkan berbagai cara untuk mengekspresikan rentang pengumpulan:

Ekspresi rentang operator Deskripsi
.. Semua nilai dalam koleksi.
..end Nilai dari awal hingga secara end eksklusif.
start.. Nilai dari secara start inklusif hingga akhir.
start..end Nilai mulai dari start secara inklusif hingga end secara eksklusif.
^start.. Nilai dari start secara inklusif hingga ke akhir, dihitung dari akhir.
..^end Nilai dari awal hingga end dengan pengecualian menghitung dari akhir.
start..^end Nilai dari start secara inklusif hingga end dihitung secara eksklusif dari akhir.
^start..^end Nilai dari start secara inklusif hingga end secara eksklusif keduanya dihitung dari akhir.

Contoh berikut menunjukkan efek penggunaan semua rentang yang disajikan dalam tabel sebelumnya:

int[] oneThroughTen =
[
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
];

Write(oneThroughTen, ..);
Write(oneThroughTen, ..3);
Write(oneThroughTen, 2..);
Write(oneThroughTen, 3..5);
Write(oneThroughTen, ^2..);
Write(oneThroughTen, ..^3);
Write(oneThroughTen, 3..^4);
Write(oneThroughTen, ^4..^2);

static void Write(int[] values, Range range) =>
    Console.WriteLine($"{range}:\t{string.Join(", ", values[range])}");
// Sample output:
//      0..^0:      1, 2, 3, 4, 5, 6, 7, 8, 9, 10
//      0..3:       1, 2, 3
//      2..^0:      3, 4, 5, 6, 7, 8, 9, 10
//      3..5:       4, 5
//      ^2..^0:     9, 10
//      0..^3:      1, 2, 3, 4, 5, 6, 7
//      3..^4:      4, 5, 6
//      ^4..^2:     7, 8

Untuk informasi selengkapnya, lihat Indeks dan rentang.

Token .. juga digunakan untuk elemen penyebaran dalam ekspresi koleksi.

Kelebihan beban operator

Operator ., (), ^, dan .. tidak dapat kelebihan beban. Operator [] juga dianggap sebagai operator yang tidak dapat di-overload. Gunakan pengindeks untuk mendukung pengindeksan dengan jenis yang ditentukan pengguna.

Spesifikasi bahasa C#

Untuk informasi selengkapnya, lihat bagian berikut dari spesifikasi bahasa C#:

Untuk informasi selengkapnya tentang indeks dan rentang, lihat catatan usulan fitur tersebut.

Lihat juga