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.
A partir de C# 14, las declaraciones no genéricas de nivel superior static class
pueden utilizar contenedores extension
para declarar miembros de extensión. Los miembros de extensión son métodos o propiedades y pueden parecer ser miembros estáticos o de instancia. Las versiones anteriores de C# habilitan los métodos de extensión agregando this
como modificador al primer parámetro de un método estático declarado en una clase estática de nivel superior y no genérico.
El extension
bloque especifica el tipo y el destinatario de los miembros de la extensión. Puede declarar métodos y propiedades dentro de la extension
declaración. En el ejemplo siguiente se declara un único bloque de extensión que define un método de extensión de instancia y una propiedad de instancia.
public static class NumericSequences
{
extension(IEnumerable<int> sequence)
{
public IEnumerable<int> AddValue(int operand)
{
foreach (var item in sequence)
{
yield return item + operand;
}
}
public int Median
{
get
{
var sortedList = sequence.OrderBy(n => n).ToList();
int count = sortedList.Count;
int middleIndex = count / 2;
if (count % 2 == 0)
{
// Even number of elements: average the two middle elements
return (sortedList[middleIndex - 1] + sortedList[middleIndex]);
}
else
{
// Odd number of elements: return the middle element
return sortedList[middleIndex];
}
}
}
public int this[int index] => sequence.Skip(index).First();
}
}
extension
define el receptor: sequence
, que es un IEnumerable<int>
. El tipo de receptor puede ser no genérico, un genérico abierto o un tipo genérico cerrado. El nombre sequence
está en el ámbito de cada miembro de instancia declarado en esa extensión. Tanto el método de extensión como la propiedad acceden a sequence
.
Se puede tener acceso a cualquiera de los miembros de la extensión como si fueran miembros del tipo de receptor:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
var median = numbers.Median;
Puede declarar cualquier número de miembros en un único contenedor, siempre que compartan el mismo receptor. También puede declarar tantos bloques de extensión en una sola clase. No es necesario que diferentes extensiones declaren el mismo tipo o nombre del receptor. El parámetro de extensión no necesita incluir el nombre del parámetro si los únicos miembros son estáticos:
extension(IEnumerable<int>)
{
// Method:
public static IEnumerable<int> Generate(int low, int count, int increment)
{
for (int i = 0; i < count; i++)
yield return low + (i * increment);
}
// Property:
public static IEnumerable<int> Identity => Enumerable.Empty<int>();
}
Se puede llamar a las extensiones estáticas como si fuesen miembros estáticos del tipo receptor.
var newSequence = IEnumerable<int>.Generate(5, 10, 2);
var identity = IEnumerable<int>.Identity;
Importante
Una extensión no introduce un ámbito para las declaraciones de miembro. Todos los miembros declarados en una sola clase, incluso si en varias extensiones, deben tener firmas únicas. La firma generada incluye el tipo de receptor en su nombre para los miembros estáticos y el parámetro receptor para los miembros de instancia de extensión.
En el ejemplo siguiente se muestra un método de extensión mediante el this
modificador :
public static class NumericSequenceExtensionMethods
{
public static IEnumerable<int> AddValue(this IEnumerable<int> sequence, int operand)
{
foreach (var item in sequence)
yield return item + operand;
}
}
Se puede llamar al Add
método desde cualquier otro método como si fuera miembro de la IEnumerable<int>
interfaz:
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
Ambas formas de métodos de extensión generan el mismo lenguaje intermedio (IL). Los autores de llamadas no pueden distinguir entre ellos. De hecho, puede transformar los métodos de extensión existentes en la nueva sintaxis de miembro sin causar un cambio importante. Los formatos son binarios y compatibles con el origen.
Bloques de extensión genéricos
Cuando especifique los parámetros de tipo para un miembro de extensión declarado en un bloque de extensión depende de dónde se requiera ese parámetro de tipo:
- Agregue el parámetro tipo a la declaración
extension
cuando se utilice el parámetro tipo en el receptor. - Agregue el parámetro de tipo a la declaración de miembro cuando el tipo sea distinto de cualquier parámetro de tipo especificado en el receptor.
- No se puede especificar el mismo parámetro de tipo en ambas ubicaciones.
En el ejemplo siguiente se muestra un bloque de extensión para IEnumerable<T>
, donde dos de los miembros de extensión requieren un segundo parámetro de tipo:
public static class GenericExtensions
{
extension<TReceiver>(IEnumerable<TReceiver> source)
{
public IEnumerable<TReceiver> Spread(int start, int count)
=> source.Skip(start).Take(count);
public IEnumerable<TReceiver> Append<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
{
foreach(TReceiver item in source)
{
yield return item;
}
foreach (TArg item in second)
{
yield return Converter(item);
}
}
public IEnumerable<TReceiver> Prepend<TArg>(IEnumerable<TArg> second, Func<TArg, TReceiver> Converter)
{
foreach (TArg item in second)
{
yield return Converter(item);
}
foreach (TReceiver item in source)
{
yield return item;
}
}
}
}
Los miembros Append
y Prepend
especifican el parámetro de tipo adicional para la conversión. Ninguno de los miembros repite el parámetro de tipo para el receptor.
Las declaraciones de métodos de extensión equivalentes muestran cómo se codifican esos parámetros de tipo:
public static class GenericExtensions
{
public static IEnumerable<T> Spread<T>(this IEnumerable<T> source, int start, int count)
=> source.Skip(start).Take(count);
public static IEnumerable<T1> Append<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> second, Func<T2, T1> Converter)
{
foreach (T1 item in source)
{
yield return item;
}
foreach (T2 item in second)
{
yield return Converter(item);
}
}
public static IEnumerable<T1> Prepend<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> second, Func<T2, T1> Converter)
{
foreach (T2 item in second)
{
yield return Converter(item);
}
foreach (T1 item in source)
{
yield return item;
}
}
}
Consulte también
Especificación del lenguaje C#
Para obtener más información, consulte la Especificación del lenguaje C#. La especificación del lenguaje es el origen definitivo de la sintaxis y el uso de C#.