Operadores y expresiones de acceso a miembros (referencia de C#)
Puede usar los operadores y expresiones siguientes cuando accede a un miembro de tipo:
.(acceso a miembros): para acceder a un miembro de un espacio de nombres o un tipo[](elemento de matriz o acceso a indizador): para acceder a un elemento de matriz o un indizador de tipo?.y?[](operadores condicionales NULL): para realizar una operación de acceso a elementos o miembros solo si un operando es distinto de NULL()(invocación): para llamar a un método de acceso o invocar un delegado^(índice desde el final) : para indicar que la posición del elemento se encuentra a partir del final de una secuencia..(intervalo): para especificar un intervalo de índices que puede utilizar para obtener un intervalo de elementos de secuencia
Expresión de acceso a miembros.
Use el token . para acceder a un miembro de un espacio de nombres o un tipo, como se muestran en los ejemplos siguientes:
- Use
.para acceder a un espacio de nombres anidado dentro de un espacio de nombres, como se muestra en el siguiente ejemplo de una directivausing:
using System.Collections.Generic;
- Use
.para formar un nombre completo para tener acceso a un tipo dentro de un espacio de nombres, como se muestra en el código siguiente:
System.Collections.Generic.IEnumerable<int> numbers = new int[] { 1, 2, 3 };
Utilice una directiva using para hacer que el uso de nombres completos sea opcional.
- Use
.para tener acceso a los miembros de tipo, que no son estáticos y, como se muestra en el código siguiente:
var constants = new List<double>();
constants.Add(Math.PI);
constants.Add(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 del indizador []
Los corchetes, [], se suelen usar para el acceso a matriz, indizador o elemento de puntero.
Acceso a matriz
En el ejemplo siguiente se muestra cómo se obtiene acceso a los 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
Si un índice de matriz se encuentra fuera de los límites de la dimensión correspondiente de una matriz, se produce una excepción IndexOutOfRangeException.
Tal como se muestra en el ejemplo anterior, también usa corchetes al declarar un tipo de matriz o crear instancias de matriz.
Para obtener más información sobre las matrices, consulte Matrices.
Acceso a indizador
En el ejemplo siguiente se usa el tipo Dictionary<TKey,TValue> de .NET para mostrar el acceso al indizador:
var dict = new Dictionary<string, double>();
dict["one"] = 1;
dict["pi"] = Math.PI;
Console.WriteLine(dict["one"] + dict["pi"]); // output: 4.14159265358979
Los indizadores le permiten indizar las instancias de un tipo definido por el usuario de un modo similar a la indización de matrices. A diferencia de los índices de matriz, que deben ser enteros, los parámetros de indizador se pueden declarar para ser de cualquier tipo.
Para más información sobre los indizadores, consulte Indizadores.
Otros usos de []
Para información sobre el acceso de los elementos de puntero, consulte la sección Operador de acceso de elemento de puntero del artículo Operadores relacionados con el puntero.
También usa los corchetes para especificar atributos:
[System.Diagnostics.Conditional("DEBUG")]
void TraceMethod() {}
Operadores condicionales NULL ?. y ?[]
Disponible en C# 6 y versiones posteriores, un operador condicional NULL aplica una operación de acceso a miembros, ?., o acceso a elementos, ?[], a su operando solo si dicho operando se evalúa como no null. Es decir:
Si
ase evalúa comonull, el resultado dea?.xoa?[x]esnull.Si
ase evalúa como no NULL, el resultado dea?.xoa?[x]es el mismo que el resultado dea.xoa[x], respectivamente.Nota
Si
a.xoa[x]producen una excepción,a?.xoa?[x]produciría la misma excepción paraano NULL. Por ejemplo, siaes una instancia de matriz que no es NULL yxestá fuera de los límites dea,a?[x]produciría una excepción IndexOutOfRangeException.
Los operadores de condición NULL se cortocircuitan. Es decir, si una operación en una cadena de la operación de acceso a elementos o miembros condicional devuelve null, no se ejecuta el resto de la cadena. En el ejemplo siguiente, 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 lo serían si A no lo es también, solo tiene que aplicar el operador condicional NULL a A:
A?.B.C();
En el ejemplo anterior, B no se evalúa y no se llama a C() 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
var numberSets = new List<double[]>
{
new[] { 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
using System;
using System.Collections.Generic;
using System.Linq;
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 FullName Name { get; set; }
}
public class FullName
{
public string FirstName { get; set; }
public string LastName { get; set; }
public void Write()
{
Console.WriteLine($"{FirstName} {LastName}");
}
}
}
En el primero de los dos ejemplos anteriores 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 que no admite un valor NULL, T, a?.x o a?[x] es del tipo de valor que admite un valor NULL T? correspondiente. Si necesita una expresión de tipo T, aplique el operador de fusión de NULL ?? a una expresión condicional NULL, tal 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(new int[0])); // output: 0
Console.WriteLine(GetSumOfFirstTwoOrDefault(new[] { 3, 4, 5 })); // output: 7
En el ejemplo anterior, si no utiliza el operador ??, numbers?.Length < 2 da como resultado false cuando numbers es null.
El operador de acceso de miembro condicional NULL ?. también se conoce con el nombre de operador Elvis.
Invocación de delegado seguro para subprocesos
Use el operador ?. para comprobar si un delegado es distinto de NULL y se invoca de forma segura para subprocesos (por ejemplo, cuando se genera un evento), tal como se muestra en el código siguiente:
PropertyChanged?.Invoke(…)
Ese código es equivalente al código siguiente que se usaría en C# 5 o una versión anterior:
var handler = this.PropertyChanged;
if (handler != null)
{
handler(…);
}
Es un modo seguro para subprocesos de asegurarse de que solo se invoca un handler que no es NULL. Dado que las instancias de delegado son inmutables, ningún subproceso puede cambiar el valor al que hace referencia la variable local handler. En concreto, si el código que ha ejecutado otro subproceso cancela la suscripción del evento PropertyChanged y PropertyChanged se convierte en null antes de que se invoque handler, el objeto al que hace referencia handler queda intacto. 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.
Expresión de invocación ()
Utilice paréntesis, (), para llamar a un método o invocar un delegado.
En el ejemplo siguiente se muestra cómo llamar a un método, con o sin argumentos, y cómo invocar un delegado:
Action<int> display = s => Console.WriteLine(s);
var numbers = new List<int>();
numbers.Add(10);
numbers.Add(17);
display(numbers.Count); // output: 2
numbers.Clear();
display(numbers.Count); // output: 0
También usa paréntesis al invocar un constructor con el operador new.
Otros usos de ()
También usa los paréntesis para ajustar el orden en el que se van a evaluar operaciones en una expresión. Para obtener más información, vea Operadores de C# (referencia de C#).
Expresiones de conversión, que realizan conversiones de tipo explícitas, también utilizan paréntesis.
Indexación desde el operador final ^
Disponible en C# 8.0 y versiones posteriores, el operador ^ indica la posición del elemento a partir del final de una secuencia. En el caso de una secuencia de longitud length, ^n apunta al elemento con desplazamiento length - n desde el inicio de una secuencia. Por ejemplo, ^1 apunta al último elemento de una secuencia y ^length apunta al primer elemento de una secuencia.
int[] xs = new[] { 0, 10, 20, 30, 40 };
int last = xs[^1];
Console.WriteLine(last); // output: 40
var lines = new List<string> { "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 del tipo System.Index. En la expresión ^e, el resultado de e debe poderse convertir implícitamente a int.
También puede usar el operador ^ con el operador de intervalo para crear un intervalo de índices. Para más información, consulte Índices y rangos.
Operador de intervalo .
Disponible en C# 8.0 y versiones posteriores, 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 inicio o desde el final de una secuencia, tal y como muestra el ejemplo siguiente:
int[] numbers = new[] { 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 del tipo System.Range. En la expresión a..b, los resultados de a y b deben poderse convertir implícitamente a int o Index.
Puede omitir cualquiera de los operandos del operador .. para obtener un intervalo abierto:
a..es equivalente aa..^0..bes equivalente a0..b..es equivalente a0..^0
int[] numbers = new[] { 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));
Para más información, consulte Índices y rangos.
Posibilidad de sobrecarga del operador
Los operadores ., (), ^ y .. no se pueden sobrecargar. El operador [] también se considera un operador que no se puede sobrecargar. Use indizadores para admitir la indizació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 más información sobre índices y rangos, vea la nota de propuesta de características.