C# 14부터 최상위 비제네릭 static class 선언은 블록을 사용하여 extension확장 멤버를 선언할 수 있습니다. 확장 멤버는 메서드 또는 속성이며 인스턴스 또는 정적 멤버로 표시될 수 있습니다. 이전 버전의 C#에서는 최상위 비제네릭 정적 클래스에 선언된 정적 메서드의 첫 번째 매개 변수에 한정자로 추가하여 this 메서드를 사용하도록 설정합니다.
블록은 extension 확장 멤버의 형식과 수신기를 지정합니다. 선언 내에서 메서드, 속성 또는 연산자를 선언할 extension 수 있습니다. 다음 예제에서는 인스턴스 확장 메서드, 인스턴스 속성 및 정적 연산자 메서드를 정의하는 단일 확장 블록을 선언합니다.
비고
이 문서의 모든 예제에는 멤버 및 확장 블록에 대한 XML 주석이 포함됩니다. 블록의 노드는 extension 확장 형식 및 수신기 매개 변수를 설명합니다. C# 컴파일러는 확장 블록의 모든 멤버에 대해 생성된 멤버에 이 노드를 복사합니다. 다음 예제에서는 확장 멤버에 대한 XML 설명서를 생성하기 위한 기본 스타일을 보여 줍니다.
/// <summary>
/// Contains extension members for numeric sequences.
/// </summary>
public static class NumericSequences
{
/// <summary>
/// Defines extensions for integer sequences.
/// </summary>
/// <param name="sequence">The sequence used as a receiver.</param>
extension(IEnumerable<int> sequence)
{
/// <summary>
/// Adds a scalar value to each element in the sequence.
/// </summary>
/// <param name="operand">The amount to add.</param>
/// <returns>
/// A new sequence where each value contains the updated value.
/// </returns>
public IEnumerable<int> AddValue(int operand)
{
foreach (var item in sequence)
{
yield return item + operand;
}
}
/// <summary>
/// Gets the median value of the sequence.
/// </summary>
/// <remarks>
/// This value is calculated when requested.
/// </remarks>
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];
}
}
}
/// <summary>
/// Concatenates two sequences of integers into a single sequence.
/// </summary>
/// <remarks>The resulting sequence enumerates all elements from <paramref name="left"/> in order,
/// followed by all elements from <paramref name="right"/>. Enumeration is deferred and performed lazily as the
/// returned sequence is iterated.</remarks>
/// <param name="left">The first sequence of integers to concatenate. Cannot be null.</param>
/// <param name="right">The second sequence of integers to concatenate. Cannot be null.</param>
/// <returns>A sequence that contains the elements of the first sequence followed by the
/// elements of the second sequence.</returns>
public static IEnumerable<int> operator +(IEnumerable<int> left, IEnumerable<int> right)
=> left.Concat(right);
}
}
extension은 수신기인 sequence을 정의하며, 이 수신기는 IEnumerable<int>입니다. 수신기 형식은 비제네릭, 개방형 제네릭 또는 닫힌 제네릭 형식일 수 있습니다. 이름은 sequence 해당 확장에 선언된 모든 인스턴스 멤버의 범위에 있습니다. 확장 메서드와 속성은 모두 sequence에 접근합니다.
확장 멤버는 수신기 유형의 멤버인 것처럼 액세스합니다.
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
var median = numbers.Median;
var combined = numbers + Enumerable.Range(100, 10);
동일한 수신기를 공유하는 한 단일 블록에서 멤버 수를 선언할 수 있습니다. 단일 클래스에서 확장 블록을 여러 개 선언할 수도 있습니다. 다른 확장은 수신기의 동일한 형식 또는 이름을 선언할 필요가 없습니다. 유일한 멤버가 정적인 경우 확장 매개 변수에 매개 변수 이름을 포함할 필요가 없습니다.
/// <summary>
/// Provides static extensions for the <see cref="IEnumerable{Int32}"/> type.
/// </summary>
extension(IEnumerable<int>)
{
// Method:
/// <summary>
/// Generates a sequence of integers, starting from a specified value and incrementing by a given amount.
/// </summary>
/// <param name="low">The starting value of the sequence.</param>
/// <param name="count">The number of integers to generate. Must be non-negative.</param>
/// <param name="increment">The value by which to increment each subsequent integer in the sequence.</param>
/// <returns>
/// An enumerable collection of integers, beginning with the specified starting value and containing the
/// specified number of elements, each incremented by the given amount.
/// </returns>
public static IEnumerable<int> Generate(int low, int count, int increment)
{
for (int i = 0; i < count; i++)
yield return low + (i * increment);
}
// Property:
/// <summary>
/// Gets an empty sequence of integers representing the identity element for sequence operations.
/// </summary>
/// <remarks>
/// This property can be used as a neutral starting point when aggregating or composing
/// sequences of integers. The returned sequence is always empty and does not allocate any storage.
/// </remarks>
public static IEnumerable<int> Identity => Enumerable.Empty<int>();
}
정적 확장은 수신기 유형의 정적 멤버인 것처럼 호출합니다.
var newSequence = IEnumerable<int>.Generate(5, 10, 2);
var identity = IEnumerable<int>.Identity;
연산자는 형식에서 사용자 정의 연산자인 것처럼 호출합니다.
중요합니다
확장은 멤버 선언에 대한 범위를 도입하지 않습니다. 단일 클래스에서 선언된 모든 멤버는 여러 확장에 있더라도 고유한 서명이 있어야 합니다. 생성된 서명에는 정적 멤버의 이름에 수신기 형식과 확장 인스턴스 멤버의 수신기 매개 변수가 포함됩니다.
다음 예제에서는 this 한정자를 사용한 확장 메서드를 보여줍니다.
public static class NumericSequenceExtensionMethods
{
public static IEnumerable<int> AddValue(this IEnumerable<int> sequence, int operand)
{
foreach (var item in sequence)
yield return item + operand;
}
}
인터페이스의 Add 멤버 IEnumerable<int> 인 것처럼 다른 메서드에서 메서드를 호출할 수 있습니다.
IEnumerable<int> numbers = Enumerable.Range(1, 10);
numbers = numbers.AddValue(10);
두 형태의 확장 메서드는 동일한 IL(중간 언어)을 생성합니다. 발신자는 둘을 구별할 수 없습니다. 실제로 호환성이 손상되는 변경 없이 기존 확장 메서드를 새 멤버 구문으로 변환할 수 있습니다. 형식들은 이진 형식과 소스 코드 모두와 호환됩니다.
일반 확장 블록
확장 블록에 선언된 확장 멤버에 대한 형식 매개 변수를 지정하는 위치는 형식 매개 변수가 필요한 위치에 따라 달라집니다.
- 형식 매개 변수가 수신기에서
extension사용될 때 선언에 형식 매개 변수를 추가합니다. - 형식이 수신기에 지정된 형식 매개 변수와 다른 경우 멤버 선언에 형식 매개 변수를 추가합니다.
- 두 위치 모두에서 동일한 형식 매개 변수를 지정할 수 없습니다.
다음 예제에서는 IEnumerable<T> 확장 블록의 멤버 중 두 개가 두 번째 형식 매개 변수를 필요로 하는 경우를 보여줍니다.
/// <summary>
/// Contains generic extension members for sequences.
/// </summary>
public static class GenericExtensions
{
/// <summary>
/// Defines extensions for generic sequences.
/// </summary>
/// <typeparam name="TReceiver">The type of elements in the receiver sequence.</typeparam>
/// <param name="source">The sequence used as a receiver.</param>
extension<TReceiver>(IEnumerable<TReceiver> source)
{
/// <summary>
/// Returns a sequence containing a specified number of elements from the source, starting at a given index.
/// </summary>
/// <param name="start">The zero-based index at which to begin retrieving elements. Must be greater than or equal to 0.</param>
/// <param name="count">The number of elements to return. Must be greater than or equal to 0.</param>
/// <returns>
/// An <see cref="IEnumerable{TReceiver}"/> that contains up to <paramref name="count"/> elements from the
/// source sequence, starting at the element at position <paramref name="start"/>. If <paramref name="start"/>
/// is greater than the number of elements in the source, an empty sequence is returned.
/// </returns>
public IEnumerable<TReceiver> Spread(int start, int count)
=> source.Skip(start).Take(count);
/// <summary>
/// Returns a sequence that contains the elements of the original sequence followed by the elements of a
/// specified sequence, each transformed by a converter function.
/// </summary>
/// <remarks>
/// Enumeration of the returned sequence will not start until the sequence is iterated.
/// The converter function is applied to each element of the appended sequence as it is enumerated.
/// </remarks>
/// <typeparam name="TArg">The type of the elements in the sequence to append.</typeparam>
/// <param name="second">The sequence whose elements are to be appended after being converted. Cannot be null.</param>
/// <param name="Converter">A function to convert each element of the appended sequence to the result type. Cannot be null.</param>
/// <returns>
/// An IEnumerable<TReceiver> that contains the elements of the original sequence followed by the converted
/// elements of the specified sequence.
/// </returns>
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);
}
}
/// <summary>
/// Returns a sequence that consists of the elements of the specified collection, transformed by the provided
/// converter, followed by the elements of the current sequence.
/// </summary>
/// <remarks>
/// Enumeration of the returned sequence will not start until the sequence is iterated.
/// Both the input collection and the converter function must not be null; otherwise, an exception will be
/// thrown at enumeration time.
/// </remarks>
/// <typeparam name="TArg">The type of the elements in the collection to prepend.</typeparam>
/// <param name="second">The collection whose elements are to be transformed and prepended to the current sequence. Cannot be null.</param>
/// <param name="converter">A function to convert each element of the prepended collection to the target type. Cannot be null.</param>
/// <returns>
/// An IEnumerable<TReceiver> that contains the converted elements of the specified collection followed by the
/// elements of the current sequence.
/// </returns>
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;
}
}
}
}
멤버 Append 를 지정하고 Prepend 변환에 대한 추가 형식 매개 변수를 지정합니다. 어떤 멤버도 수신기에 대한 형식 매개 변수를 반복하지 않습니다.
해당하는 확장 메서드 선언은 이러한 형식 매개 변수를 인코딩하는 방법을 보여 줍니다.
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;
}
}
}
참고하십시오
C# 언어 사양
자세한 내용은 C# 언어 사양을 참조하세요. 언어 사양은 C# 구문 및 사용의 최종 소스입니다.
.NET