Operatory i wyrażenia dostępu do składowych — operatory kropki, indeksatora i wywołania.
Aby uzyskać dostęp do elementu członkowskiego typu, należy użyć kilku operatorów i wyrażeń. Te operatory obejmują dostęp do składowych (.
), element tablicy lub dostęp indeksatora ([]
), indeks-from-end (), zakres (^
..
), operatory warunkowe o wartości null (?.
i ?[]
) oraz wywołanie metody (()
). Należą do nich operatory dostępu warunkowego o wartości null (?.
) i dostępu indeksatora (?[]
).
.
(dostęp do składowych): aby uzyskać dostęp do członka przestrzeni nazw lub typu[]
(dostęp do elementu tablicy lub indeksatora): aby uzyskać dostęp do elementu tablicy lub indeksatora typów?.
i?[]
(operatory warunkowe o wartości null): aby wykonać operację dostępu do elementu członkowskiego lub elementu tylko wtedy, gdy operand ma wartość inną niż null()
(wywołanie): wywołanie metody dostępu lub wywołanie delegata^
(indeks od końca): aby wskazać, że pozycja elementu pochodzi od końca sekwencji..
(zakres): aby określić zakres indeksów, których można użyć do uzyskania zakresu elementów sekwencji
Wyrażenie dostępu do składowych .
Token służy do uzyskiwania .
dostępu do elementu członkowskiego przestrzeni nazw lub typu, jak pokazano w poniższych przykładach:
- Użyj polecenia
.
, aby uzyskać dostęp do zagnieżdżonej przestrzeni nazw w przestrzeni nazw, jak pokazano w poniższym przykładzieusing
dyrektywy :
using System.Collections.Generic;
- Użyj
.
polecenia , aby utworzyć kwalifikowaną nazwę , aby uzyskać dostęp do typu w przestrzeni nazw, jak pokazano w poniższym kodzie:
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];
using
Użyj dyrektywy , aby korzystać z nazw kwalifikowanych jako opcjonalne.
- Użyj
.
polecenia , aby uzyskać dostęp do składowych typu, statycznych i niestatycznych, jak pokazano w poniższym kodzie:
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
Możesz również użyć .
metody , aby uzyskać dostęp do metody rozszerzenia.
Operator indeksatora []
Nawiasy kwadratowe, []
, są zwykle używane do dostępu do tablicy, indeksatora lub wskaźnika. Począwszy od języka C# 12, []
otacza wyrażenie kolekcji.
Dostęp do tablicy
W poniższym przykładzie pokazano, jak uzyskać dostęp do elementów tablicy:
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
Jeśli indeks tablicy znajduje się poza granicami odpowiadającego wymiaru tablicy, IndexOutOfRangeException jest zwracany element .
Jak pokazano w poprzednim przykładzie, używane są również nawiasy kwadratowe podczas deklarowania typu tablicy lub tworzenia wystąpienia wystąpienia tablicy.
Aby uzyskać więcej informacji na temat tablic, zobacz Tablice.
Dostęp indeksatora
W poniższym przykładzie użyto typu .NET Dictionary<TKey,TValue> , aby zademonstrować dostęp indeksatora:
var dict = new Dictionary<string, double>();
dict["one"] = 1;
dict["pi"] = Math.PI;
Console.WriteLine(dict["one"] + dict["pi"]); // output: 4.14159265358979
Indeksatory umożliwiają indeksowanie wystąpień typu zdefiniowanego przez użytkownika w podobny sposób jak indeksowanie tablicy. W przeciwieństwie do indeksów tablicowych, które muszą być liczbą całkowitą, parametry indeksatora można zadeklarować jako dowolny typ.
Aby uzyskać więcej informacji na temat indeksatorów, zobacz Indeksatory.
Inne użycie []
Aby uzyskać informacje o dostępie do elementu wskaźnika, zobacz sekcję Operator dostępu do elementu wskaźnika [] artykułu Operatory powiązane ze wskaźnikiem. Aby uzyskać informacje na temat wyrażeń kolekcji, zobacz artykuł dotyczący wyrażeń kolekcji .
Można również użyć nawiasów kwadratowych, aby określić atrybuty:
[System.Diagnostics.Conditional("DEBUG")]
void TraceMethod() {}
Operatory warunkowe ?.
o wartości null i ?[]
Operator warunkowy o wartości null stosuje operację dostępu do składowej (?.
) lub dostępu do elementu (?[]
) do operandu tylko wtedy, gdy operand zwróci wartość inną niż null; w przeciwnym razie zwraca wartość null
. Innymi słowy:
Jeśli
a
wartość zostanie obliczona nanull
wartość , wynik parametrua?.x
luba?[x]
ma wartośćnull
.Jeśli
a
wartość ma wartość inną niż null, wynika?.x
luba?[x]
jest taki sam jak wynika.x
luba[x]
, odpowiednio.Uwaga
Jeśli
a.x
luba[x]
zgłosi wyjątek luba?.x
a?[x]
zgłosi ten sam wyjątek dla wartości innej niż nulla
. Jeśli na przykłada
jest wystąpieniem tablicy o wartości innej niż null ix
znajduje się poza granicamia
,a?[x]
zgłasza błąd IndexOutOfRangeException.
Operatory warunkowe o wartości null są zwarciem. Oznacza to, że jeśli jedna operacja w łańcuchu operacji dostępu warunkowego lub elementu elementu zwraca null
wartość , pozostała część łańcucha nie jest wykonywana. W poniższym przykładzie nie jest obliczana, B
jeśli A
ocenia wartość null
i C
nie jest oceniana, czy lub B
A
ocenia null
wartość :
A?.B?.Do(C);
A?.B?[C];
Jeśli A
wartość może mieć wartość null, C
ale B
nie będzie mieć wartości null, jeśli wartość A nie ma wartości null, wystarczy zastosować operator warunkowy o wartości null do A
polecenia :
A?.B.C();
W poprzednim przykładzie B
nie jest obliczany i C()
nie jest wywoływany, jeśli A
ma wartość null. Jeśli jednak dostęp do łańcuchowego elementu członkowskiego zostanie przerwany, na przykład przez nawiasy, tak jak w przypadku (A?.B).C()
, zwarcie nie nastąpi.
W poniższych przykładach pokazano użycie operatorów ?.
i ?[]
:
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}");
}
Pierwszy z powyższych dwóch przykładów używa również operatora ??
łączenia wartości null, aby określić alternatywne wyrażenie do oceny w przypadku, gdy wynikiem operacji warunkowej null jest null
.
Jeśli a.x
lub a[x]
jest typu T
wartości innej niż null, a?.x
lub a?[x]
ma odpowiadający typ T?
wartości dopuszczanej do wartości null . Jeśli potrzebujesz wyrażenia typu T
, zastosuj operator ??
łączenia wartości null do wyrażenia warunkowego o wartości null, jak pokazano w poniższym przykładzie:
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
W poprzednim przykładzie, jeśli nie używasz ??
operatora, numbers?.Length < 2
ocenia, false
kiedy numbers
ma wartość null
.
Uwaga
Operator ?.
ocenia operand po lewej stronie nie więcej niż raz, gwarantując, że nie można go zmienić na null
po zweryfikowaniu jako nie null.
Operator ?.
dostępu warunkowego o wartości null jest również znany jako operator Elvis.
Wywołanie delegata bezpiecznego wątkowo
?.
Użyj operatora , aby sprawdzić, czy delegat nie ma wartości null i wywołać go w bezpieczny wątkowo sposób (na przykład podczas zgłaszania zdarzenia), jak pokazano w poniższym kodzie:
PropertyChanged?.Invoke(…)
Ten kod jest odpowiednikiem następującego kodu:
var handler = this.PropertyChanged;
if (handler != null)
{
handler(…);
}
Powyższy przykład to bezpieczny wątkowo sposób, aby upewnić się, że wywoływana jest tylko wartość inną niż null handler
. Ponieważ wystąpienia delegatów są niezmienne, żaden wątek nie może zmienić obiektu przywoływanego przez zmienną lokalną handler
. W szczególności, jeśli kod wykonywany przez inny wątek anuluje PropertyChanged
null
subskrypcję zdarzenia i PropertyChanged
stanie się przed handler
wywołaniem, obiekt, handler
do którego odwołuje się obiekt, pozostaje bez wpływu.
Wyrażenie wywołania ()
Użyj nawiasów, ()
, aby wywołać metodę lub wywołać delegata.
W poniższym przykładzie pokazano, jak wywołać metodę z argumentami lub bez argumentów i wywołać delegata:
Action<int> display = s => Console.WriteLine(s);
List<int> numbers =
[
10,
17
];
display(numbers.Count); // output: 2
numbers.Clear();
display(numbers.Count); // output: 0
Nawiasy są również używane podczas wywoływania konstruktora z operatorem new
.
Inne zastosowania ()
Nawiasy służą również do dostosowywania kolejności oceniania operacji w wyrażeniu. Aby uzyskać więcej informacji, zobacz Operatory języka C#.
Wyrażenia rzutowe, które wykonują jawne konwersje typów, również używają nawiasów.
Indeksowanie z operatora końcowego ^
Operatory indeksu i zakresu mogą być używane z typem, który można liczyć. Typ zliczalny to typ, który ma int
właściwość o nazwie Count
lub Length
z dostępnym get
akcesorem. Wyrażenia kolekcji bazują również na typach zliczalnych.
Operator ^
wskazuje położenie elementu z końca sekwencji. W przypadku sekwencji długości length
^n
wskazuje element z przesunięciem length - n
od początku sekwencji. Na przykład ^1
wskazuje ostatni element sekwencji i ^length
wskazuje pierwszy element sekwencji.
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
Jak pokazano w poprzednim przykładzie, wyrażenie ^e
jest System.Index typu . W wyrażeniu ^e
e
wynik musi być niejawnie konwertowany na int
.
Możesz również użyć ^
operatora z operatorem zakresu, aby utworzyć zakres indeksów. Aby uzyskać więcej informacji, zobacz Indeksy i zakresy.
Począwszy od języka C# 13, indeks z operatora końcowego może być używany w inicjatorze obiektów.
Operator zakresu ..
Operator ..
określa początek i koniec zakresu indeksów jako operandy. Operand po lewej stronie jest inkluzywnym początkiem zakresu. Operand po prawej stronie to ekskluzywny koniec zakresu. Jeden z operandów może być indeksem od początku lub od końca sekwencji, jak pokazano w poniższym przykładzie:
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));
Jak pokazano w poprzednim przykładzie, wyrażenie a..b
jest System.Range typu . W wyrażeniu a..b
a
wyniki i b
muszą być niejawnie konwertowane na Int32 lub Index.
Ważne
Niejawne konwersje z int
, aby zgłosić Index
ArgumentOutOfRangeException wartość, gdy wartość jest ujemna.
Aby uzyskać zakres otwarty, można pominąć dowolny z operandów ..
operatora:
a..
jest odpowiednikiema..^0
..b
jest odpowiednikiem0..b
..
jest odpowiednikiem0..^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));
W poniższej tabeli przedstawiono różne sposoby wyrażania zakresów kolekcji:
Wyrażenie operatora zakresu | opis |
---|---|
.. |
Wszystkie wartości w kolekcji. |
..end |
Wartości od początku do end wyłącznie. |
start.. |
Wartości z start inkluzywnie do końca. |
start..end |
Wartości z start inkluzywnie do end wyłącznie. |
^start.. |
Wartości z start inkluzywnie do końca liczone od końca. |
..^end |
Wartości od początku do end wyłącznie liczone od końca. |
start..^end |
Wartości z start inkluzywnego do end wyłącznie zliczania od końca. |
^start..^end |
Wartości z start inkluzywnego do end wyłącznie zliczania od końca. |
W poniższym przykładzie pokazano efekt użycia wszystkich zakresów przedstawionych w poprzedniej tabeli:
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
Aby uzyskać więcej informacji, zobacz Indeksy i zakresy.
Token ..
jest również używany dla elementu spread w wyrażeniu kolekcji.
Przeciążenie operatora
Operatory .
, ()
, ^
i ..
nie mogą być przeciążone. Operator []
jest również uważany za operator nienaładowalny. Indeksatory umożliwiają obsługę indeksowania z typami zdefiniowanymi przez użytkownika.
specyfikacja języka C#
Aby uzyskać więcej informacji, zobacz następujące sekcje specyfikacji języka C#:
Aby uzyskać więcej informacji na temat indeksów i zakresów, zobacz notatkę dotyczącą propozycji funkcji.