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å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å ett using 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 till nullär nullresultatet av a?.x eller a?[x] .

  • Om a utvärderas till icke-null är resultatet av a?.x eller a?[x] detsamma som resultatet av a.x eller a[x], respektive.

    Kommentar

    Om a.x eller a[x] utlöser ett undantag a?.x eller a?[x] utlöser samma undantag för icke-null a. Om till exempel a är en icke-null-matrisinstans och x ligger utanför gränserna afö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 nullkö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 Tinte kan nullvärde , a?.x eller a?[x] är av motsvarande null-värdetyp T?. Om du behöver ett uttryck av typen Tanvä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 lengthpekar ^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 ^emå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..bmå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.. motsvarar a..^0
  • ..b motsvarar 0..b
  • .. motsvarar 0..^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.

Se även