Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Se usan varios operadores y expresiones para acceder a un miembro de tipo. Los operadores de acceso a miembros incluyen el acceso a miembros (.
), el elemento de matriz o el acceso al indexador ([]
), los operadores index-from-end (^
), range (..
), null-conditional (?.
y ?[]
), y la invocación del método (()
). Estos operadores incluyen los operadores de acceso a miembros condicionales NULL (?.
) y acceso al indexador (?[]
).
-
.
(acceso a miembros): para acceder a un miembro de un espacio de nombres o un tipo -
[]
(acceso a un elemento de matriz o indexador): para tener acceso a un elemento de matriz o a un indexador de tipos -
?.
y?[]
(operadores condicionales NULL): para realizar una operación de acceso de miembro o elemento solo si un operando no es NULL -
()
(invocación): para llamar a un método al que se tiene acceso o invocar un delegado -
^
(índice desde el final): para indicar que la posición del elemento se cuenta desde el final de una secuencia -
..
(range): para especificar un intervalo de índices que puede usar para obtener un intervalo de elementos de secuencia
Expresión de acceso a miembros .
Usted usa el token .
para acceder a un miembro de un espacio de nombres o de un tipo, como demuestran los ejemplos siguientes:
- Use
.
para acceder a un espacio de nombres anidado dentro de otro espacio de nombres, tal como se muestra en el siguiente ejemplo de una directivausing
:
using System.Collections.Generic;
- Use
.
para formar un nombre completo para acceder a un tipo dentro de un espacio de nombres, como se muestra en el código siguiente:
System.Collections.Generic.IEnumerable<int> numbers = [1, 2, 3];
Use una using
directiva para hacer opcional el uso de nombres calificados.
- Use
.
para acceder a miembros de tipo, estáticos y no estáticos, como se muestra en el código siguiente:
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
También puede usar .
para acceder a un método de extensión.
Operador de índice []
Los corchetes, []
, se usan normalmente para el acceso a elementos de matriz, indexador o puntero. A partir de C# 12, []
incluye una expresión de colección.
Acceso a matriz
En el ejemplo siguiente se muestra cómo acceder a los elementos de la 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
Si un índice de matriz está fuera de los límites de la dimensión correspondiente de una matriz, se lanza una IndexOutOfRangeException.
Como se muestra en el ejemplo anterior, también se usan corchetes al declarar un tipo de arreglo o instanciar una matriz.
Para obtener más información sobre las matrices, vea Matrices.
Acceso a indizador
En el ejemplo siguiente se usa el tipo de .NET Dictionary<TKey,TValue> para mostrar el acceso del indexador:
var dict = new Dictionary<string, double>();
dict["one"] = 1;
dict["pi"] = Math.PI;
Console.WriteLine(dict["one"] + dict["pi"]); // output: 4.14159265358979
Los indexadores permiten indexar instancias de un tipo definido por el usuario de forma similar a la indexación de matriz. A diferencia de los índices de matriz, que deben ser enteros, los parámetros del indexador se pueden declarar como de cualquier tipo.
Para obtener más información sobre los indexadores, consulte Indexadores.
Otros usos de []
Para obtener información sobre el acceso al elemento de puntero, consulte la sección Operador de acceso al elemento puntero [] del artículo Operadores relacionados con el puntero . Para obtener información sobre las expresiones de colección, consulte el artículo expresiones de colección .
También se usan corchetes para especificar atributos:
[System.Diagnostics.Conditional("DEBUG")]
void TraceMethod() {}
Además, los corchetes pueden usarse para designar patrones de lista y utilizarlos en la coincidencia de patrones o pruebas.
arr is ([1, 2, ..])
//Specifies that an array starts with (1, 2)
Operadores null condicionales ?.
y ?[]
Un operador condicional null aplica una operación de acceso de miembro (?.
) o acceso a elementos (?[]
) a su operando solo si este se evalúa como no nulo; de lo contrario, devuelve null
. En otras palabras:
Si
a
se evalúa comonull
, el resultado dea?.x
oa?[x]
esnull
.Si
a
se evalúa como no NULL, el resultado dea?.x
oa?[x]
es el mismo que el resultado dea.x
oa[x]
, respectivamente.Nota:
Si
a.x
oa[x]
produce una excepción,a?.x
oa?[x]
produciría la misma excepción para un valor distinto de NULLa
. Por ejemplo, sia
es una instancia de matriz que no es null yx
está fuera de los límites dea
,a?[x]
produciría un IndexOutOfRangeException.
Los operadores de condición NULL se cortocircuitan. Es decir, si una operación en una cadena de acceso a elementos o miembros condicionales devuelve null
, el resto de la cadena no se ejecuta. En el siguiente ejemplo, B
no se evalúa si A
se evalúa como null
y C
no se evalúa si A
o B
se evalúan como null
.
A?.B?.Do(C);
A?.B?[C];
Si A
podría ser NULL pero B
y C
no sería NULL si A no es NULL, solo tiene que aplicar el operador condicional null a A
:
A?.B.C();
En el ejemplo anterior, B
no se evalúa y C()
no se llama a si A
es NULL. Sin embargo, si se interrumpe el acceso a miembros encadenados, por ejemplo, entre paréntesis como en (A?.B).C()
, no se produciría un cortocircuito.
En los ejemplos siguientes se muestra el uso de los ?.
operadores y ?[]
:
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}");
}
En el primer ejemplo anterior también se usa el operador de fusión de NULL ??
para especificar una expresión alternativa que se evaluará en caso de que el resultado de la operación condicional NULL sea null
.
Si a.x
o a[x]
es de un tipo de valor no anulable T
, entonces a?.x
o a?[x]
es del tipo de valor anulable correspondienteT?
. Si necesita una expresión de tipo T
, aplique el operador ??
de fusión de nulos a una expresión condicional nula, así como se muestra en el ejemplo siguiente:
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
En el ejemplo anterior, si no usa el ??
operador , numbers?.Length < 2
se evalúa como false
cuando numbers
es null
.
Nota:
El operador ?.
evalúa el operando de la izquierda no más de una vez, lo que garantiza que no se pueda cambiar a null
después de verificarse como no NULL.
A partir de C# 14, la asignación está permitida con una expresión de acceso condicional null (?.
y ?[]
) en los tipos de referencia. Por ejemplo, consulte el método siguiente:
person?.FirstName = "Scott";
messages?[5] = "five";
En el ejemplo anterior se muestra la asignación a una propiedad y un elemento indizado en un tipo de referencia que podría ser NULL. Un comportamiento importante para esta asignación es que la expresión del lado derecho del =
se evalúa solo cuando se confirma que el lado izquierdo no es nulo. Por ejemplo, en el código siguiente, solo se llama a la función GenerateNextIndex
cuando la values
matriz no es null. Si la values
matriz es nula, GenerateNextIndex
no se llama.
person?.FirstName = "Scott";
messages?[5] = "five";
En otras palabras, el código anterior es equivalente al código siguiente mediante una if
instrucción para la comprobación null:
if (values is not null)
{
values[2] = GenerateNextIndex();
}
Además de la asignación, se permite cualquier forma de asignación compuesta, como +=
o -=
. Sin embargo, no se permiten incrementos (++
) ni decrementos (--
).
Esta mejora no clasifica una expresión condicional nula como una variable. No se puede asignar con ref
, ni se puede asignar a una variable ref
ni pasar a un método como argumento ref
o out
.
Invocación de delegado seguro para subprocesos
Use el ?.
operador para comprobar si un delegado no es NULL e invoquelo de forma segura para subprocesos (por ejemplo, cuando se genera un evento), como se muestra en el código siguiente:
PropertyChanged?.Invoke(…)
Ese código es equivalente al código siguiente:
var handler = this.PropertyChanged;
if (handler != null)
{
handler(…);
}
El ejemplo anterior es una manera segura para subprocesos para asegurarse de que solo se invoca un valor no NULL handler
. Dado que las instancias de delegado son inmutables, ningún subproceso puede cambiar el objeto al que hace referencia la handler
variable local. En concreto, si el código ejecutado por otro subproceso cancela la suscripción del PropertyChanged
evento y PropertyChanged
se vuelve null
antes handler
de invocarse, el objeto al que hace handler
referencia sigue sin verse afectado.
Expresión de invocación ()
Use paréntesis, ()
, para llamar a un método o invocar un delegado.
El código siguiente muestra cómo llamar a un método, con o sin argumentos, e invocar un 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
También se usan paréntesis al invocar un constructor con el new
operador .
Otros usos de ()
También se usan paréntesis para ajustar el orden en el que se evalúan las operaciones de una expresión. Para obtener más información, consulte Operadores de C#.
Las expresiones de conversión, que realizan conversiones de tipo explícitas, también utilizan paréntesis.
Indexación desde el operador final ^
Los operadores de índice e intervalo se pueden usar con un tipo contable. Un tipo contable es un tipo que tiene una propiedad int
denominada Count
o Length
con un descriptor de acceso get
accesible.
Las expresiones de colección también se basan en tipos que se pueden contar .
Nota:
Las matrices unidimensionales se pueden contar. **
Las matrices multidimensionales no existen. Los ^
operadores y ..
(intervalo) no se pueden usar en matrices multidimensionales.
El ^
operador indica la posición del elemento desde el final de una secuencia. Para una secuencia de longitud length
, ^n
apunta al elemento con desplazamiento length - n
desde el principio de una secuencia. Por ejemplo, ^1
apunta al último elemento de una secuencia y ^length
apunta al primer elemento de una secuencia.
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 se muestra en el ejemplo anterior, la expresión ^e
es de tipo System.Index. En la expresión ^e
, el resultado de e
debe convertirse implícitamente en int
.
También puede usar el ^
operador con el operador de rango para crear un rango de índices. Para obtener más información, vea Índices y rangos.
A partir de C# 13, el índice del operador final se puede usar en un inicializador de objeto.
Operador de intervalo ..
El ..
operador especifica el inicio y el final de un intervalo de índices como sus operandos. El operando izquierdo es un inicio inclusivo de un intervalo. El operando derecho es un inicio exclusivo de un intervalo. Cualquiera de los operandos puede ser un índice desde el principio o desde el final de una secuencia, como se muestra en el ejemplo siguiente:
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 se muestra en el ejemplo anterior, la expresión a..b
es de tipo System.Range. En la expresión a..b
, los resultados de a
y b
deben convertirse implícitamente en Int32 o Index.
Importante
Las conversiones implícitas de int
a Index
lanzan una ArgumentOutOfRangeException cuando el valor es negativo.
Puede omitir cualquiera de los operandos del ..
operador para obtener un intervalo abierto:
-
a..
es equivalente aa..^0
-
..b
es equivalente a0..b
-
..
es 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));
En la tabla siguiente se muestran varias maneras de expresar intervalos de colección:
Expresión del operador Range | Descripción |
---|---|
.. |
Todos los valores de la colección. |
..end |
Valores desde el principio hasta end exclusivamente. |
start.. |
Valores desde start inclusivamente hasta el final. |
start..end |
Valores desde start inclusivamente hasta end exclusivamente. |
^start.. |
Valores desde start inclusivamente hasta el final contando desde el final. |
..^end |
Valores desde el inicio hasta end exclusivamente contando desde el final. |
start..^end |
Valores de start inclusivamente a end exclusivamente contando desde el final. |
^start..^end |
Valores de start inclusivamente a end exclusivamente contando ambos desde el final. |
En el ejemplo siguiente se muestra el efecto de usar todos los intervalos presentados en la tabla 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 obtener más información, vea Índices y rangos.
El token ..
también se usa para el elemento de propagación en una expresión de colección.
Sobrecarga del operador
Los .
, ()
, ^
y ..
operadores no se pueden sobrecargar. El operador []
también se considera un operador no sobrecargable. Use indexadores para admitir la indexación con tipos definidos por el usuario.
Especificación del lenguaje C#
Para más información, vea las secciones siguientes de la Especificación del lenguaje C#:
Para obtener más información sobre índices y rangos, consulte la nota de propuesta de características.