Operatorer och uttryck för medlemsåtkomst – operatorerna dot, indexerare och anrop.
Du använder flera operatorer och uttryck för att komma åt en typmedlem. Dessa operatorer omfattar medlemsåtkomst (.
), matriselement eller indexeraråtkomst ([]
), index från slutpunkt (^
), intervall (..
), null-villkorsstyrda operatorer (?.
och ?[]
) och metodanrop (()
). Dessa inkluderar operatorerna null-villkorlig medlemsåtkomst (?.
) och indexerareåtkomst (?[]
).
.
(medlemsåtkomst): för att få åtkomst till en medlem i ett namnområde eller en typ[]
(åtkomst till matriselement eller indexerare): för att komma åt ett matriselement eller en typindexerare?.
och?[]
(null-villkorsstyrda operatorer): att utföra en medlems- eller elementåtkomståtgärd endast om en operand inte är null()
(anrop): anropa en åtkomstmetod eller anropa ett ombud^
(index från slutet): för att indikera att elementpositionen kommer från slutet av en sekvens..
(intervall): för att ange ett intervall med index som du kan använda för att hämta ett intervall med sekvenselement
Medlemsåtkomstuttryck .
Du använder .
token för att komma åt en medlem i ett namnområde eller en typ, som följande exempel visar:
- Använd
.
för att komma åt ett kapslat namnområde i ett namnområde, som följande exempel på ettusing
direktiv visar:
using System.Collections.Generic;
- Använd
.
för att skapa ett kvalificerat namn för att komma åt en typ i ett namnområde, som följande kod visar:
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];
Använd ett using
direktiv för att göra det valfritt att använda kvalificerade namn.
- Använd
.
för att komma åt typmedlemmar, statiska och icke-statiska, som följande kod visar:
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
Du kan också använda .
för att komma åt en tilläggsmetod.
Indexeraren []
Hakparenteser, []
, används vanligtvis för åtkomst till matriser, indexerare eller pekarelement. Från och med C# 12 omsluter []
ett samlingsuttryck.
Matrisåtkomst
I följande exempel visas hur du kommer åt matriselement:
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
Om ett matrisindex ligger utanför gränserna för motsvarande dimension i en matris genereras en IndexOutOfRangeException .
Som föregående exempel visar använder du även hakparenteser när du deklarerar en matristyp eller instansierar en matrisinstans.
Mer information om matriser finns i Matriser.
Indexerarens åtkomst
I följande exempel används .NET-typen Dictionary<TKey,TValue> för att demonstrera indexerarens åtkomst:
var dict = new Dictionary<string, double>();
dict["one"] = 1;
dict["pi"] = Math.PI;
Console.WriteLine(dict["one"] + dict["pi"]); // output: 4.14159265358979
Med indexerare kan du indexera instanser av en användardefinierad typ på liknande sätt som matrisindexering. Till skillnad från matrisindex, som måste vara heltal, kan indexerarparametrarna deklareras vara av vilken typ som helst.
Mer information om indexerare finns i Indexerare.
Andra användningar av []
Information om åtkomst till pekarelement finns i avsnittet Åtkomstoperator för pekarelement [] i artikeln Pekarerelaterade operatorer . Information om samlingsuttryck finns i artikeln samlingsuttryck .
Du använder också hakparenteser för att ange attribut:
[System.Diagnostics.Conditional("DEBUG")]
void TraceMethod() {}
Null-villkorsstyrda operatorer ?.
och ?[]
En null-villkorsoperator tillämpar endast en åtgärd för medlemsåtkomst (?.
) eller elementåtkomst (?[]
) på dess operande om operand utvärderas till icke-null. Annars returneras null
. Med andra ord:
Om
a
utvärderas tillnull
ärnull
resultatet ava?.x
ellera?[x]
.Om
a
utvärderas till icke-null är resultatet ava?.x
ellera?[x]
detsamma som resultatet ava.x
ellera[x]
, respektive.Kommentar
Om
a.x
ellera[x]
utlöser ett undantaga?.x
ellera?[x]
utlöser samma undantag för icke-nulla
. Om till exempela
är en icke-null-matrisinstans ochx
ligger utanför gränsernaa
för ,a?[x]
skulle utlösa en IndexOutOfRangeException.
De null-villkorsstyrda operatorerna kortsluter. Om en åtgärd i en kedja med villkorsstyrda medlems- eller elementåtkomståtgärder returnerar null
körs inte resten av kedjan. I följande exempel B
utvärderas inte om A
utvärderas till null
och C
utvärderas inte om A
eller B
utvärderas till null
:
A?.B?.Do(C);
A?.B?[C];
Om A
kan vara null men B
inte C
är null om A inte är null, behöver du bara tillämpa null-villkorsoperatorn på A
:
A?.B.C();
I föregående exempel B
utvärderas inte och C()
anropas inte om A
är null. Men om den länkade medlemsåtkomsten avbryts, till exempel av parenteser som i (A?.B).C()
, sker inte kortslutning.
Följande exempel visar användningen av operatorerna ?.
och ?[]
:
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}");
}
Det första av de föregående två exemplen använder också operatorn ??
null-coalescing för att ange ett alternativt uttryck för att utvärdera om resultatet av en null-villkorsstyrd åtgärd är null
.
Om a.x
eller a[x]
är av en värdetyp som T
inte kan nullvärde , a?.x
eller a?[x]
är av motsvarande null-värdetyp T?
. Om du behöver ett uttryck av typen T
använder du operatorn ??
null-coalescing på ett null-villkorsuttryck, som följande exempel visar:
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
Om du inte använder operatorn ??
numbers?.Length < 2
i föregående exempel utvärderas till false
när numbers
är null
.
Kommentar
Operatorn ?.
utvärderar sin vänstra operande högst en gång, vilket garanterar att den inte kan ändras till efter att null
ha verifierats som icke-null.
Den null-villkorliga medlemsåtkomstoperatorn ?.
kallas även Elvis-operatorn.
Trådsäkert ombudsanrop
Använd operatorn ?.
för att kontrollera om ett ombud inte är null och anropa det på ett trådsäkert sätt (till exempel när du genererar en händelse), som följande kod visar:
PropertyChanged?.Invoke(…)
Koden motsvarar följande kod:
var handler = this.PropertyChanged;
if (handler != null)
{
handler(…);
}
Föregående exempel är ett trådsäkert sätt att se till att endast en icke-null handler
anropas. Eftersom delegerade instanser är oföränderliga kan ingen tråd ändra objektet som refereras av den handler
lokala variabeln. I synnerhet om koden som körs av en annan tråd avbryter prenumerationen PropertyChanged
på händelsen och PropertyChanged
blir null
innan handler
anropas, förblir objektet som refereras av handler
opåverkat.
Anropsuttryck ()
Använd parenteser, ()
, för att anropa en metod eller anropa ett ombud.
I följande exempel visas hur du anropar en metod, med eller utan argument, och anropar ett ombud:
Action<int> display = s => Console.WriteLine(s);
List<int> numbers =
[
10,
17
];
display(numbers.Count); // output: 2
numbers.Clear();
display(numbers.Count); // output: 0
Du använder också parenteser när du anropar en konstruktor med operatorn new
.
Andra användningar av ()
Du använder också parenteser för att justera ordningen för att utvärdera åtgärder i ett uttryck. Mer information finns i C#-operatorer.
Cast-uttryck, som utför explicita typkonverteringar, använder också parenteser.
Index från slutoperatorn ^
Index- och intervalloperatorer kan användas med en typ som kan räknas. En countable-typ är en typ som har en int
egenskap med namnet antingen Count
eller Length
med en tillgänglig get
accessor. Samlingsuttryck förlitar sig också på antalstyper .
Operatorn ^
anger elementpositionen från slutet av en sekvens. För en sekvens med längd length
pekar ^n
på elementet med förskjutning length - n
från början av en sekvens. Pekar till exempel ^1
på det sista elementet i en sekvens och ^length
pekar på det första elementet i en sekvens.
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
Som föregående exempel visar är uttrycket ^e
av typen System.Index . I uttryck ^e
måste resultatet av e
vara implicit konvertibelt till int
.
Du kan också använda operatorn ^
med intervalloperatorn för att skapa ett indexintervall. Mer information finns i Index och intervall.
Från och med C# 13 kan indexet från slutoperatorn användas i en objektinitierare.
Intervalloperator ..
Operatorn ..
anger början och slutet av ett indexintervall som operander. Den vänstra operanden är en inkluderande start på ett intervall. Den högra operand är en exklusiv ände av ett intervall. Någon av operanderna kan vara ett index från början eller från slutet av en sekvens, som följande exempel visar:
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));
Som föregående exempel visar är uttrycket a..b
av typen System.Range . I uttryck a..b
måste resultatet av a
och b
implicit konverteras till Int32 eller Index.
Viktigt!
Implicita konverteringar från int
till att Index
utlösa en ArgumentOutOfRangeException när värdet är negativt.
Du kan utelämna någon av operatorns ..
operander för att få ett öppet intervall:
a..
motsvarara..^0
..b
motsvarar0..b
..
motsvarar0..^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));
I följande tabell visas olika sätt att uttrycka samlingsintervall:
Intervalloperatoruttryck | beskrivning |
---|---|
.. |
Alla värden i samlingen. |
..end |
Värden från början till exklusivt end . |
start.. |
Värden från inkluderande start till slutpunkt. |
start..end |
Värden från inkluderande start till end exklusivt. |
^start.. |
Värden från inkluderande start till sluträkning från slutet. |
..^end |
Värden från början till uteslutande end räkning från slutet. |
start..^end |
Värden från start inkluderande till end uteslutande räkning från slutet. |
^start..^end |
Värden från start inkluderande till end uteslutande båda räknar från slutet. |
I följande exempel visas effekten av att använda alla intervall som visas i föregående tabell:
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
Mer information finns i Index och intervall.
Token ..
används också för spread-elementet i ett samlingsuttryck.
Överlagring av operator
Operatorerna .
, ()
, ^
och ..
kan inte överbelastas. Operatorn []
anses också vara en icke-överbelastningsbar operator. Använd indexerare för att stödja indexering med användardefinierade typer.
Språkspecifikation för C#
Mer information finns i följande avsnitt i C#-språkspecifikationen:
Mer information om index och intervall finns i funktionsförslagsanteckningen.