Operadores de acesso de membro e expressões - os operadores de ponto, indexador e invocação.
Você usa vários operadores e expressões para acessar um membro do tipo. Esses operadores incluem acesso de membro (.
), acesso de elemento de matriz ou indexador ([]
), index-from-end (^
), range (..
), operadores condicionais nulos (?.
e ?[]
) e invocação de método (()
). Estes incluem os operadores null-conditional member access (?.
) e indexer access (?[]
).
.
(acesso de membro): para acessar um membro de um namespace ou um tipo[]
(elemento de matriz ou acesso de indexador): para acessar um elemento de matriz ou um indexador de tipo?.
e?[]
(operadores condicionais nulos): para executar uma operação de acesso de membro ou elemento somente se um operando não for nulo()
(invocação): para chamar um método acessado ou invocar um delegado^
(índice do fim): para indicar que a posição do elemento é do final de uma sequência..
(intervalo): para especificar um intervalo de índices que você pode usar para obter um intervalo de elementos de sequência
Expressão de acesso de membro .
Você usa o .
token para acessar um membro de um namespace ou um tipo, como demonstram os exemplos a seguir:
- Use
.
para acessar um namespace aninhado dentro de um namespace, como mostra o exemplo de diretivausing
a seguir:
using System.Collections.Generic;
- Use
.
para formar um nome qualificado para acessar um tipo dentro de um namespace, como mostra o código a seguir:
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];
Use uma using
diretiva para tornar opcional o uso de nomes qualificados.
- Use
.
para acessar membros do tipo, estáticos e não estáticos, como mostra o código a seguir:
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
Você também pode usar .
para acessar um método de extensão.
Operador indexador []
Os colchetes, []
, são normalmente usados para acesso a matrizes, indexadores ou elementos de ponteiro. Começando com C# 12, []
inclui uma expressão de coleção.
Acesso ao array
O exemplo a seguir demonstra como acessar elementos de matriz:
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
Se um índice de matriz estiver fora dos limites da dimensão correspondente de uma matriz, um IndexOutOfRangeException será lançado.
Como mostra o exemplo anterior, você também usa colchetes quando declara um tipo de matriz ou instancia uma instância de matriz.
Para obter mais informações sobre matrizes, consulte Matrizes.
Acesso ao indexador
O exemplo a seguir usa o tipo .NET Dictionary<TKey,TValue> para demonstrar o acesso do indexador:
var dict = new Dictionary<string, double>();
dict["one"] = 1;
dict["pi"] = Math.PI;
Console.WriteLine(dict["one"] + dict["pi"]); // output: 4.14159265358979
Os indexadores permitem indexar instâncias de um tipo definido pelo usuário da mesma forma que a indexação de matrizes. Ao contrário dos índices de matriz, que devem ser inteiros, os parâmetros do indexador podem ser declarados como sendo de qualquer tipo.
Para obter mais informações sobre indexadores, consulte Indexadores.
Outros usos de []
Para obter informações sobre o acesso ao elemento de ponteiro, consulte a seção Operador de acesso ao elemento de ponteiro [] do artigo Operadores relacionados ao ponteiro. Para obter informações sobre expressões de coleção, consulte o artigo de expressões de coleção.
Você também usa colchetes para especificar atributos:
[System.Diagnostics.Conditional("DEBUG")]
void TraceMethod() {}
Operadores ?.
condicionais nulos e ?[]
Um operador condicional nulo aplica uma operação de acesso de membro (?.
) ou de acesso de elemento (?[]
) ao seu operando somente se esse operando for avaliado como não-nulo, caso contrário, ele retornará null
. Por outras palavras:
Se
a
avalia anull
, o resultado dea?.x
oua?[x]
énull
.Se
a
for avaliado como não-nulo, o resultado dea?.x
oua?[x]
é o mesmo que o resultado dea.x
oua[x]
, respectivamente.Nota
Se
a.x
oua[x]
lança uma exceção,a?.x
oua?[x]
lançaria a mesma exceção para não-nuloa
. Por exemplo, sea
for uma instância de matriz não nula ex
estiver fora dos limites dea
,a?[x]
lançaria um IndexOutOfRangeExceptionarquivo .
Os operadores condicionais nulos são curto-circuitos. Ou seja, se uma operação em uma cadeia de operações de acesso a membros ou elementos condicionais retornar null
, o restante da cadeia não será executado. No exemplo a seguir, B
não é avaliado se A
avalia para null
e C
não é avaliado se A
ou B
avalia como null
:
A?.B?.Do(C);
A?.B?[C];
Se A
pode ser null, mas B
e C
não seria null se A não for null, você só precisa aplicar o operador null-conditional a A
:
A?.B.C();
No exemplo anterior, B
não é avaliado e C()
não é chamado se A
for nulo. No entanto, se o acesso do membro encadeado for interrompido, por exemplo, entre parênteses como no (A?.B).C()
, o curto-circuito não acontece.
Os exemplos a seguir demonstram o uso dos ?.
operadores e ?[]
:
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}");
}
O primeiro dos dois exemplos anteriores também usa o operador ??
null-coalescing para especificar uma expressão alternativa para avaliar caso o resultado de uma operação null-conditional seja null
.
Se a.x
ou a[x]
é de um tipo T
de valor não anulável , a?.x
ou a?[x]
é do tipo T?
de valor anulável correspondente . Se você precisar de uma expressão do tipo T
, aplique o operador ??
null-coalescing a uma expressão null-condicional, como mostra o exemplo a seguir:
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
No exemplo anterior, se você não usar o ??
operador, numbers?.Length < 2
avalia quando false
numbers
é null
.
Nota
O ?.
operador avalia seu operando esquerdo no máximo uma vez, garantindo que ele não possa ser alterado para null
depois de ser verificado como não nulo.
O operador ?.
de acesso de membro nulo e condicional também é conhecido como operador Elvis.
Invocação de delegado thread-safe
Use o ?.
operador para verificar se um delegado não é nulo e invoque-o de forma segura para threads (por exemplo, quando você gera um evento), como mostra o código a seguir:
PropertyChanged?.Invoke(…)
Esse código é equivalente ao seguinte código:
var handler = this.PropertyChanged;
if (handler != null)
{
handler(…);
}
O exemplo anterior é uma maneira segura de threads para garantir que apenas um não-nulo handler
seja invocado. Como as instâncias delegadas são imutáveis, nenhum thread pode alterar o objeto referenciado handler
pela variável local. Em particular, se o código executado por outro thread cancelar a PropertyChanged
inscrição do evento e PropertyChanged
se tornar null
antes handler
é invocado, o objeto referenciado por handler
permanece inalterado.
Expressão de invocação ()
Use parênteses, ()
, para chamar um método ou invocar um delegado.
O exemplo a seguir demonstra como chamar um método, com ou sem argumentos, e invocar um delegado:
Action<int> display = s => Console.WriteLine(s);
List<int> numbers =
[
10,
17
];
display(numbers.Count); // output: 2
numbers.Clear();
display(numbers.Count); // output: 0
Você também usa parênteses quando invoca um construtor com o new
operador.
Outros usos de ()
Você também usa parênteses para ajustar a ordem na qual avaliar operações em uma expressão. Para obter mais informações, consulte Operadores C#.
As expressões de transmissão, que executam conversões de tipo explícitas, também usam parênteses.
Índice do operador final ^
Os operadores de índice e intervalo podem ser usados com um tipo que é contável. Um tipo contável é um tipo que tem uma int
propriedade chamada ou Count
Length
com um acessador acessível get
. As expressões de coleção também dependem de tipos contáveis .
O ^
operador indica a posição do elemento a partir do final de uma sequência. Para uma sequência de comprimento length
, ^n
aponta para o elemento com deslocamento length - n
desde o início de uma sequência. Por exemplo, ^1
aponta para o último elemento de uma sequência e ^length
aponta para o primeiro elemento de uma sequência.
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
Como mostra o exemplo anterior, a expressão ^e
é do System.Index tipo. Em expressão ^e
, o resultado de e
deve ser implicitamente conversível em int
.
Você também pode usar o ^
operador com o operador de intervalo para criar um intervalo de índices. Para obter mais informações, consulte Índices e intervalos.
A partir do C# 13, o índice do operador final pode ser usado em um inicializador de objeto.
Operador de gama ..
O ..
operador especifica o início e o fim de um intervalo de índices como seus operandos. O operando esquerdo é um início inclusivo de um intervalo. O operando do lado direito é uma extremidade exclusiva de um intervalo. Qualquer um dos operandos pode ser um índice do início ou do final de uma sequência, como mostra o exemplo a seguir:
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));
Como mostra o exemplo anterior, a expressão a..b
é do System.Range tipo. Em expressão a..b
, os resultados de a
e b
devem ser implicitamente convertíveis em Int32 ou Index.
Importante
Conversões implícitas de int
para Index
lançar um ArgumentOutOfRangeException quando o valor é negativo.
Você pode omitir qualquer um dos operandos do ..
operador para obter um intervalo aberto:
a..
é equivalente aa..^0
..b
é equivalente a0..b
..
é equivalente a0..^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));
A tabela a seguir mostra várias maneiras de expressar intervalos de coleção:
Expressão do operador de intervalo | Description |
---|---|
.. |
Todos os valores na coleção. |
..end |
Valores desde o início até o end exclusivo. |
start.. |
Valores desde o start inclusivo até ao fim. |
start..end |
Valores do start inclusivo ao end exclusivo. |
^start.. |
Valores desde o start inclusivo até o final contando a partir do final. |
..^end |
Valores desde o início até à end contagem exclusiva a partir do fim. |
start..^end |
Valores de start inclusivo a end exclusivamente contando a partir do final. |
^start..^end |
Valores de start inclusivos a end exclusivamente ambos contando a partir do final. |
O exemplo a seguir demonstra o efeito do uso de todos os intervalos apresentados na tabela anterior:
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
Para obter mais informações, consulte Índices e intervalos.
O ..
token também é usado para o elemento spread em uma expressão de coleção.
Capacidade de sobrecarga do operador
Os .
operadores , ()
, ^
e ..
não podem ser sobrecarregados. O []
operador também é considerado um operador não sobrecarregado. Use indexadores para dar suporte à indexação com tipos definidos pelo usuário.
Especificação da linguagem C#
Para obter mais informações, consulte as seguintes seções da especificação da linguagem C#:
Para obter mais informações sobre índices e intervalos, consulte a nota de proposta de recurso.