모든 LINQ 기반 메서드는 두 가지 유사한 패턴 중 하나를 따릅니다. 열거 가능한 시퀀스를 수행합니다. 다른 시퀀스 또는 단일 값을 반환합니다. 셰이프의 일관성을 사용하면 비슷한 셰이프로 메서드를 작성하여 LINQ를 확장할 수 있습니다. 실제로 .NET 라이브러리는 LINQ가 처음 도입된 이후 많은 .NET 릴리스에서 새로운 메서드를 얻었습니다. 이 문서에서는 동일한 패턴을 따르는 고유한 메서드를 작성하여 LINQ를 확장하는 예제를 볼 수 있습니다.
LINQ 쿼리에 대한 사용자 지정 메서드 추가
인터페이스에 확장 메서드를 추가하여 LINQ 쿼리에 사용하는 메서드 집합을 IEnumerable<T> 확장합니다. 예를 들어 표준 평균 또는 최대 작업 외에도 값 시퀀스에서 단일 값을 계산하는 사용자 지정 집계 메서드를 만듭니다. 또한 값 시퀀스에 대한 사용자 지정 필터 또는 특정 데이터 변환으로 작동하고 새 시퀀스를 반환하는 메서드를 만듭니다. 이러한 메서드의 예는 Distinct, Skip및 Reverse.
인터페이스를 IEnumerable<T> 확장할 때 열거 가능한 컬렉션에 사용자 지정 메서드를 적용할 수 있습니다. 자세한 내용은 확장 메서드를 참조하세요.
집계 메서드는 값 집합에서 단일 값을 계산합니다. LINQ는 , Average및 Min.를 비롯한 Max여러 집계 메서드를 제공합니다. 인터페이스에 확장 메서드를 추가하여 고유한 집계 메서드를 IEnumerable<T> 만들 수 있습니다.
C# 14부터 확장 블록을 선언하여 여러 확장 멤버를 포함할 수 있습니다. 키워드 extension
와 수신기 매개 변수를 괄호로 하여 확장 블록을 선언합니다. 다음 코드 예제에서는 확장 블록에서 호출 Median
된 확장 메서드를 만드는 방법을 보여 있습니다. 이 메서드는 형식 double
의 숫자 시퀀스에 대한 중앙값을 계산합니다.
extension(IEnumerable<double>? source)
{
public double Median()
{
if (source is null || !source.Any())
{
throw new InvalidOperationException("Cannot compute median for a null or empty set.");
}
var sortedList =
source.OrderBy(number => number).ToList();
int itemIndex = sortedList.Count / 2;
if (sortedList.Count % 2 == 0)
{
// Even number of items.
return (sortedList[itemIndex] + sortedList[itemIndex - 1]) / 2;
}
else
{
// Odd number of items.
return sortedList[itemIndex];
}
}
}
정적 메서드에 this
한정자를 추가하여 확장 메서드를 선언할 수도 있습니다. 다음 코드는 동등한 Median
확장 메서드를 보여 큽니다.
public static class EnumerableExtension
{
public static double Median(this IEnumerable<double>? source)
{
if (source is null || !source.Any())
{
throw new InvalidOperationException("Cannot compute median for a null or empty set.");
}
var sortedList =
source.OrderBy(number => number).ToList();
int itemIndex = sortedList.Count / 2;
if (sortedList.Count % 2 == 0)
{
// Even number of items.
return (sortedList[itemIndex] + sortedList[itemIndex - 1]) / 2;
}
else
{
// Odd number of items.
return sortedList[itemIndex];
}
}
}
인터페이스에서 다른 집계 메서드를 호출하는 것과 동일한 방식으로 열거 가능한 컬렉션에 대해 확장 메서드를 IEnumerable<T> 호출합니다.
다음 코드 예제에서는 형식Median
의 배열에 메서드를 사용 double
하는 방법을 보여 있습니다.
double[] numbers = [1.9, 2, 8, 4, 5.7, 6, 7.2, 0];
var query = numbers.Median();
Console.WriteLine($"double: Median = {query}");
// This code produces the following output:
// double: Median = 4.85
다양한 형식의 시퀀스를 허용하도록 집계 메서드를 오버로드 할 수 있습니다. 표준 방법은 각 형식에 대한 오버로드를 만드는 것입니다. 또 다른 방법은 제네릭 형식을 사용하는 오버로드를 만들고 대리자를 사용하여 특정 형식으로 변환하는 것입니다. 두 방법을 모두 결합할 수도 있습니다.
지원하려는 각 형식에 대한 특정 오버로드를 만들 수 있습니다. 다음 코드 예제에서는 형식에 Median
대한 메서드의 오버로드를 보여 있습니다 int
.
// int overload
public static double Median(this IEnumerable<int> source) =>
(from number in source select (double)number).Median();
이제 다음 코드와 같이 형식과 Median
형식 모두 integer
에 대한 오버로드를 호출 double
할 수 있습니다.
double[] numbers1 = [1.9, 2, 8, 4, 5.7, 6, 7.2, 0];
var query1 = numbers1.Median();
Console.WriteLine($"double: Median = {query1}");
int[] numbers2 = [1, 2, 3, 4, 5];
var query2 = numbers2.Median();
Console.WriteLine($"int: Median = {query2}");
// This code produces the following output:
// double: Median = 4.85
// int: Median = 3
제네릭 개체 시퀀스를 허용하는 오버로드를 만들 수도 있습니다. 이 오버로드는 대리자를 매개 변수로 사용하여 제네릭 형식의 개체 시퀀스를 특정 형식으로 변환합니다.
다음 코드는 대리자를 매개 변수로 사용하는 Median
메서드의 Func<T,TResult> 오버로드를 보여줍니다. 이 대리자는 제네릭 형식 T의 개체를 사용하고 형식 double
의 개체를 반환합니다.
// generic overload
public static double Median<T>(
this IEnumerable<T> numbers, Func<T, double> selector) =>
(from num in numbers select selector(num)).Median();
이제 모든 형식의 Median
개체 시퀀스에 대한 메서드를 호출할 수 있습니다. 형식에 고유한 메서드 오버로드가 없는 경우 대리자 매개 변수를 전달해야 합니다. C#에서는 이 용도로 람다 식을 사용할 수 있습니다. 또한 Visual Basic에서 메서드 호출 대신 또는 Aggregate
절을 사용하는 Group By
경우에만 이 절 범위에 있는 값이나 식을 전달할 수 있습니다.
다음 예제 코드는 정수 배열 및 문자열 배열에 대한 메서드를 호출 Median
하는 방법을 보여 줍니다. 문자열의 경우 배열의 문자열 길이에 대한 중앙값이 계산됩니다. 이 예제에서는 각 사례에 Func<T,TResult> 대한 메서드에 대리자 매개 변수를 Median
전달하는 방법을 보여 있습니다.
int[] numbers3 = [1, 2, 3, 4, 5];
/*
You can use the num => num lambda expression as a parameter for the Median method
so that the compiler will implicitly convert its value to double.
If there is no implicit conversion, the compiler will display an error message.
*/
var query3 = numbers3.Median(num => num);
Console.WriteLine($"int: Median = {query3}");
string[] numbers4 = ["one", "two", "three", "four", "five"];
// With the generic overload, you can also use numeric properties of objects.
var query4 = numbers4.Median(str => str.Length);
Console.WriteLine($"string: Median = {query4}");
// This code produces the following output:
// int: Median = 3
// string: Median = 4
값 시 IEnumerable<T>퀀스를 반환하는 사용자 지정 쿼리 메서드를 사용하여 인터페이스를 확장할 수 있습니다. 이 경우 메서드는 형식 IEnumerable<T>의 컬렉션을 반환해야 합니다. 이러한 메서드를 사용하여 값 시퀀스에 필터 또는 데이터 변환을 적용할 수 있습니다.
다음 예제에서는 첫 번째 요소부터 시작하여 컬렉션의 다른 모든 요소를 반환하는 확장 AlternateElements
메서드를 만드는 방법을 보여줍니다.
// Extension method for the IEnumerable<T> interface.
// The method returns every other element of a sequence.
public static IEnumerable<T> AlternateElements<T>(this IEnumerable<T> source)
{
int index = 0;
foreach (T element in source)
{
if (index % 2 == 0)
{
yield return element;
}
index++;
}
}
다음 코드와 같이 인터페이스에서 다른 메서드를 호출하는 것처럼 열거 가능한 컬렉션에 IEnumerable<T> 대해 이 확장 메서드를 호출할 수 있습니다.
string[] strings = ["a", "b", "c", "d", "e"];
var query5 = strings.AlternateElements();
foreach (var element in query5)
{
Console.WriteLine(element);
}
// This code produces the following output:
// a
// c
// e
이 문서에 표시된 각 예제에는 다른 수신기가 있습니다. 즉, 각 메서드는 고유한 수신기를 지정하는 다른 확장 블록에서 선언되어야 합니다. 다음 코드 예제에서는 세 개의 서로 다른 확장 블록이 있는 단일 정적 클래스를 보여 줍니다. 각 클래스에는 이 문서에 정의된 메서드 중 하나가 포함되어 있습니다.
public static class EnumerableExtension
{
extension(IEnumerable<double>? source)
{
public double Median()
{
if (source is null || !source.Any())
{
throw new InvalidOperationException("Cannot compute median for a null or empty set.");
}
var sortedList =
source.OrderBy(number => number).ToList();
int itemIndex = sortedList.Count / 2;
if (sortedList.Count % 2 == 0)
{
// Even number of items.
return (sortedList[itemIndex] + sortedList[itemIndex - 1]) / 2;
}
else
{
// Odd number of items.
return sortedList[itemIndex];
}
}
}
extension(IEnumerable<int> source)
{
public double Median() =>
(from number in source select (double)number).Median();
}
extension<T>(IEnumerable<T> source)
{
public double Median(Func<T, double> selector) =>
(from num in source select selector(num)).Median();
public IEnumerable<T> AlternateElements()
{
int index = 0;
foreach (T element in source)
{
if (index % 2 == 0)
{
yield return element;
}
index++;
}
}
}
}
최종 확장 블록은 제네릭 확장 블록을 선언합니다. 수신기에 대한 형식 매개 변수는 자체에 extension
선언됩니다.
앞의 예제에서는 각 확장 블록에서 하나의 확장 멤버를 선언합니다. 대부분의 경우 동일한 수신기에 대해 여러 확장 멤버를 만듭니다. 이러한 경우 단일 확장 블록에서 해당 멤버에 대한 확장을 선언해야 합니다.
.NET