수색…


소개

LINQ는 tegrated Q의 uery IN L의 anguage의 약자입니다. 다양한 종류의 데이터 소스 및 형식에서 데이터를 처리하기위한 일관된 모델을 제공하여 쿼리 언어를 통합하는 개념입니다. 동일한 기본 코딩 패턴을 사용하여 XML 문서, SQL 데이터베이스, ADO.NET 데이터 집합, .NET 컬렉션 및 LINQ 공급자를 사용할 수있는 기타 형식의 데이터를 쿼리하고 변환합니다.

통사론

  • 검색어 구문 :

    • <collection>의 <range variable>에서
    • [<collection>의 <range variable>에서, ...]
    • <필터, 결합, 그룹화, 집계 연산자, ...> <람다 식>
    • <select 또는 groupBy 연산자> <결과 공식화>
  • 메소드 구문 :

    • Enumerable.Aggregate (func)
    • Enumerable.Aggregate (seed, func)
    • Enumerable.Aggregate (seed, func, resultSelector)
    • Enumerable.All (술어)
    • Enumerable.Any ()
    • Enumerable.Any (술어)
    • Enumerable.AsEnumerable ()
    • 열거 형. 평균 ()
    • 열거 형. 평균 (선택기)
    • 열거 가능. 캐스트 <결과> ()
    • Enumerable.Concat (초)
    • Enumerable.Contains (value)
    • Enumerable.Contains (value, comparer)
    • Enumerable.Count ()
    • Enumerable.Count (조건부)
    • Enumerable.DefaultIfEmpty ()
    • Enumerable.DefaultIfEmpty (defaultValue)
    • Enumerable.Distinct ()
    • Enumerable.Distinct (비교 자)
    • Enumerable.ElementAt (index)
    • Enumerable.ElementAtOrDefault (index)
    • Enumerable.Empty ()
    • Enumerable.Except (초)
    • Enumerable.Except (초, 비교 자)
    • Enumerable.First ()
    • Enumerable.First (술어)
    • Enumerable.FirstOrDefault ()
    • Enumerable.FirstOrDefault (조건 자)
    • Enumerable.GroupBy (keySelector)
    • Enumerable.GroupBy (keySelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector)
    • Enumerable.GroupBy (keySelector, comparer)
    • Enumerable.GroupBy (keySelector, resultSelector, comparer)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector)
    • Enumerable.GroupBy (keySelector, elementSelector, comparer)
    • Enumerable.GroupBy (keySelector, elementSelector, resultSelector, comparer)
    • Enumerable.Intersect (초)
    • Enumerable.Intersect (second, comparer)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector)
    • Enumerable.Join (inner, outerKeySelector, innerKeySelector, resultSelector, comparer)
    • 열거 형. 마지막 ()
    • Enumerable.Last (술어)
    • Enumerable.LastOrDefault ()
    • Enumerable.LastOrDefault (조건 자)
    • Enumerable.LongCount ()
    • Enumerable.LongCount (술어)
    • Enumerable.Max ()
    • Enumerable.Max (선택기)
    • Enumerable.Min ()
    • Enumerable.Min (선택기)
    • Enumerable.OfType <TResult> ()
    • Enumerable.OrderBy (keySelector)
    • Enumerable.OrderBy (keySelector, comparer)
    • Enumerable.OrderByDescending (keySelector)
    • Enumerable.OrderByDescending (keySelector, comparer)
    • Enumerable.Range (시작, 개수)
    • 열거 가능. 반복 (요소, 개수)
    • Enumerable.Reverse ()
    • Enumerable.Select (selector)
    • Enumerable.SelectMany (selector)
    • Enumerable.SelectMany (collectionSelector, resultSelector)
    • Enumerable.SequenceEqual (초)
    • Enumerable.SequenceEqual (second, comparer)
    • Enumerable.Single ()
    • Enumerable.Single (술어)
    • Enumerable.SingleOrDefault ()
    • Enumerable.SingleOrDefault (조건 자)
    • Enumerable.Skip (count)
    • Enumerable.SkipWhile (조건 자)
    • Enumerable.Sum ()
    • Enumerable.Sum (선택기)
    • Enumerable.Take (개수)
    • Enumerable.TakeWhile (술어)
    • orderedEnumerable.ThenBy (keySelector)
    • orderedEnumerable.ThenBy (keySelector, comparer)
    • orderedEnumerable.ThenByDescending (keySelector)
    • orderedEnumerable.ThenByDescending (keySelector, comparer)
    • 열거 형 .Array ()
    • Enumerable.ToDictionary (keySelector)
    • Enumerable.ToDictionary (keySelector, elementSelector)
    • Enumerable.ToDictionary (keySelector, comparer)
    • Enumerable.ToDictionary (keySelector, elementSelector, comparer)
    • Enumerable.ToList ()
    • Enumerable.ToLookup (keySelector)
    • Enumerable.ToLookup (keySelector, elementSelector)
    • Enumerable.ToLookup (keySelector, comparer)
    • Enumerable.ToLookup (keySelector, elementSelector, comparer)
    • Enumerable.Union (초)
    • Enumerable.Union (second, comparer)
    • 열거 형. 어디에서 (술어)
    • Enumerable.Zip (second, resultSelector)

비고

LINQ 쿼리를 사용하려면 System.Linq 을 가져와야합니다.

Method Syntax는보다 강력하고 유연하지만 Query Syntax는 더 간단하고 익숙 할 수 있습니다. Query 구문으로 작성된 모든 쿼리는 컴파일러에서 기능 구문으로 변환되므로 성능이 동일합니다.

쿼리 개체는 사용되기 전에는 평가되지 않으므로 성능 저하없이 변경 또는 추가 될 수 있습니다.

어디에

지정된 조건자가 참인 항목의 하위 집합을 반환합니다.

List<string> trees = new List<string>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };

메소드 구문

// Select all trees with name of length 3
var shortTrees = trees.Where(tree => tree.Length == 3); // Oak, Elm

쿼리 구문

var shortTrees = from tree in trees
                 where tree.Length == 3
                 select tree; // Oak, Elm

선택 - 요소 변형

Select를 사용하면 IEnumerable을 구현하는 모든 데이터 구조의 모든 요소에 변형을 적용 할 수 있습니다.

다음 목록에서 각 문자열의 첫 번째 문자 가져 오기 :

List<String> trees = new List<String>{ "Oak", "Birch", "Beech", "Elm", "Hazel", "Maple" };

일반 (람다) 구문 사용

//The below select stament transforms each element in tree into its first character.
IEnumerable<String> initials = trees.Select(tree => tree.Substring(0, 1));
foreach (String initial in initials) {
    System.Console.WriteLine(initial);
}

산출:

영형


이자형
H

.NET Fiddle에서의 라이브 데모

LINQ 쿼리 구문 사용

initials = from tree in trees
           select tree.Substring(0, 1);

연결 방법

많은 LINQ 함수는 모두 IEnumerable<TSource> 에서 작동하고 IEnumerable<TResult> 반환합니다. 형식 매개 변수 TSourceTResult 는 문제의 메서드 및 전달 된 함수에 따라 동일한 형식을 참조 할 수도 있고 그렇지 않을 수도 있습니다.

이것의 몇 가지 예가 있습니다.

public static IEnumerable<TResult> Select<TSource, TResult>(
    this IEnumerable<TSource> source,
    Func<TSource, TResult> selector
)

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, int, bool> predicate
)

public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector
)

일부 메서드 체인을 사용하면 이동하기 전에 전체 집합을 작업해야 할 수도 있지만 LINQ는 수익률 반환 MSDN 을 사용하여 연기 실행 을 활용하여 열거 형 및 열거자를 장면 뒤에 만듭니다. LINQ에서 체인을 연결하는 과정은 기본적으로 열거 형을 열거하여 구체화 할 때까지 원래 세트에 대한 열거 형 (반복자)을 작성합니다 (지연됩니다).

이렇게하면 이러한 함수가 유창하게 연결된 wiki 가 될 수 있습니다. 여기서 한 함수는 다른 함수의 결과에 직접 작용할 수 있습니다. 이 코드 스타일은 단일 명령문에서 많은 시퀀스 기반 조작을 수행하는 데 사용될 수 있습니다.

예를 들어 Select , WhereOrderBy 를 결합하여 단일 명령문에서 시퀀스를 변환, 필터링 및 정렬 할 수 있습니다.

var someNumbers = { 4, 3, 2, 1 };

var processed = someNumbers
        .Select(n => n * 2)   // Multiply each number by 2
        .Where(n => n != 6)   // Keep all the results, except for 6
        .OrderBy(n => n);     // Sort in ascending order

산출:

2
4
8

.NET Fiddle에서의 라이브 데모

일반 IEnumerable<T> 형식을 확장하고 반환하는 함수는 단일 문에서 체인 된 절로 사용할 수 있습니다. 이 유창한 프로그래밍 스타일은 강력하며, 독자적인 확장 방법을 만들 때 고려해야 합니다 .

범위 및 반복

EnumerableRangeRepeat 정적 메서드는 간단한 시퀀스를 생성하는 데 사용할 수 있습니다.

범위

Enumerable.Range() 는 시작 값과 카운트가 주어진 정수 시퀀스를 생성합니다.

// Generate a collection containing the numbers 1-100 ([1, 2, 3, ..., 98, 99, 100])
var range = Enumerable.Range(1,100);

.NET Fiddle에서의 라이브 데모

반복

Enumerable.Repeat() 는 요소와 필요한 반복 횟수를 사용하여 반복되는 요소의 시퀀스를 생성합니다.

// Generate a collection containing "a", three times (["a","a","a"])
var repeatedValues = Enumerable.Repeat("a", 3);

.NET Fiddle에서의 라이브 데모

건너 뛰기 및 가져 오기

Skip 메서드는 소스 컬렉션의 시작 부분에서 여러 항목을 제외하고 컬렉션을 반환합니다. 제외되는 항목의 수는 인수로 주어진 수입니다. 인수에 지정된 항목보다 컬렉션에 항목 수가 적은 경우 빈 컬렉션이 반환됩니다.

Take 메서드는 소스 컬렉션의 시작 부분에서 여러 요소가 포함 된 컬렉션을 반환합니다. 포함 된 항목의 수는 인수로 주어진 수입니다. 인수에 지정된 항목보다 모음에 항목이 적은 경우 반환되는 모음에는 원본 모음과 동일한 요소가 포함됩니다.

var values = new [] { 5, 4, 3, 2, 1 };

var skipTwo        = values.Skip(2);         // { 3, 2, 1 }
var takeThree      = values.Take(3);         // { 5, 4, 3 }
var skipOneTakeTwo = values.Skip(1).Take(2); // { 4, 3 }
var takeZero       = values.Take(0);         // An IEnumerable<int> with 0 items

.NET Fiddle에서의 라이브 데모

Skip and Take 는 일반적으로 다음과 같이 결과 매김을 위해 함께 사용됩니다.

IEnumerable<T> GetPage<T>(IEnumerable<T> collection, int pageNumber, int resultsPerPage) {
    int startIndex = (pageNumber - 1) * resultsPerPage;
    return collection.Skip(startIndex).Take(resultsPerPage);
}

경고 : LINQ to Entities는 정렬 된 쿼리 건너 뛰기 만 지원 합니다 . 주문하지 않고 건너 뛰기를 사용하려고하면 "Skip '메서드는 LINQ to Entities의 정렬 된 입력에 대해서만 지원되며'Skip '메서드 전에'OrderBy '메서드를 호출해야합니다."라는 메시지와 함께 NotSupportedException 이 발생합니다.

첫째, FirstOrDefault, Last, LastOrDefault, Single 및 SingleOrDefault

여섯 개의 메소드는 모두 시퀀스 유형의 단일 값을 리턴하며 술어를 사용하거나 사용하지 않고 호출 할 수 있습니다.

predicate 와 일치하는 요소의 수에 따라 또는 predicate 가 제공되지 않으면 소스 순서의 요소 수는 다음과 같이 작동합니다.

먼저()

  • 시퀀스의 첫 번째 요소 또는 제공된 predicate 일치하는 첫 번째 요소를 반환합니다.
  • 시퀀스에 요소가 없으면 "시퀀스에 요소가 없습니다"라는 메시지와 함께 InvalidOperationException 이 발생 InvalidOperationException .
  • 시퀀스에 제공된 predicate 와 일치하는 요소가 없으면 "시퀀스에 일치하는 요소가 없습니다"라는 메시지와 함께 InvalidOperationException 이 발생 InvalidOperationException .

// Returns "a":
new[] { "a" }.First();

// Returns "a":
new[] { "a", "b" }.First();

// Returns "b":
new[] { "a", "b" }.First(x => x.Equals("b"));

// Returns "ba":
new[] { "ba", "be" }.First(x => x.Contains("b"));

// Throws InvalidOperationException:
new[] { "ca", "ce" }.First(x => x.Contains("b"));

// Throws InvalidOperationException:
new string[0].First();

.NET Fiddle에서의 라이브 데모

FirstOrDefault ()

  • 시퀀스의 첫 번째 요소 또는 제공된 predicate 일치하는 첫 번째 요소를 반환합니다.
  • 시 v 스에 요소가 없거나 제공된 predicate 와 일치하는 요소가 없으면 default(T) 사용하여 default(T) 유형의 기본값을 리턴합니다.

// Returns "a":
new[] { "a" }.FirstOrDefault();

// Returns "a":
new[] { "a", "b" }.FirstOrDefault();

// Returns "b":
new[] { "a", "b" }.FirstOrDefault(x => x.Equals("b"));

// Returns "ba":
new[] { "ba", "be" }.FirstOrDefault(x => x.Contains("b"));

// Returns null:
new[] { "ca", "ce" }.FirstOrDefault(x => x.Contains("b"));

// Returns null:
new string[0].FirstOrDefault();

.NET Fiddle에서의 라이브 데모

마지막()

  • 시퀀스의 마지막 요소 또는 제공된 predicate 일치하는 마지막 요소를 반환합니다.
  • 시퀀스에 요소가 없으면 "시퀀스에 요소가 없습니다."라는 메시지와 함께 InvalidOperationException 이 발생합니다.
  • 시퀀스에 제공된 predicate 와 일치하는 요소가 없으면 "시퀀스에 일치하는 요소가 없습니다"라는 메시지와 함께 InvalidOperationException 이 발생 InvalidOperationException .

// Returns "a":
new[] { "a" }.Last();

// Returns "b":
new[] { "a", "b" }.Last();

// Returns "a":
new[] { "a", "b" }.Last(x => x.Equals("a"));

// Returns "be":
new[] { "ba", "be" }.Last(x => x.Contains("b"));

// Throws InvalidOperationException:
new[] { "ca", "ce" }.Last(x => x.Contains("b"));

// Throws InvalidOperationException:
new string[0].Last(); 

LastOrDefault ()

  • 시퀀스의 마지막 요소 또는 제공된 predicate 일치하는 마지막 요소를 반환합니다.
  • 시 v 스에 요소가 없거나 제공된 predicate 와 일치하는 요소가 없으면 default(T) 사용하여 default(T) 유형의 기본값을 리턴합니다.

// Returns "a":
new[] { "a" }.LastOrDefault();

// Returns "b":
new[] { "a", "b" }.LastOrDefault();

// Returns "a":
new[] { "a", "b" }.LastOrDefault(x => x.Equals("a"));

 // Returns "be":
new[] { "ba", "be" }.LastOrDefault(x => x.Contains("b"));

// Returns null:
new[] { "ca", "ce" }.LastOrDefault(x => x.Contains("b")); 

// Returns null:
new string[0].LastOrDefault();

단일()

  • 시 v 스가 정확히 하나의 요소 또는 제공된 predicate 와 일치하는 정확히 한 요소를 포함하는 경우, 해당 요소가 리턴됩니다.
  • 시퀀스에 요소가 없거나 제공된 predicate 와 일치하는 요소가없는 경우 "시퀀스에 요소가 없습니다"라는 메시지와 함께 InvalidOperationException 이 발생 InvalidOperationException .
  • 시퀀스에 둘 이상의 요소 또는 제공된 predicate 와 일치하는 둘 이상의 요소가 들어 있으면 "시퀀스에 둘 이상의 요소가 포함되어 있습니다"라는 메시지와 함께 InvalidOperationException 이 발생 InvalidOperationException .
  • 주 : 순서가 정확하게 1 개의 성분을 포함하고 있는지 평가하기 위하여, 많아야 2 개의 성분은 열거되어야한다.

// Returns "a":
new[] { "a" }.Single();

// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "b" }.Single();

// Returns "b":
new[] { "a", "b" }.Single(x => x.Equals("b"));

// Throws InvalidOperationException:
new[] { "a", "b" }.Single(x => x.Equals("c"));

// Throws InvalidOperationException:
new string[0].Single(); 

// Throws InvalidOperationException because sequence contains more than one element:
new[] { "a", "a" }.Single();

SingleOrDefault ()

  • 시 v 스가 정확히 하나의 요소 또는 제공된 predicate 와 일치하는 정확히 한 요소를 포함하는 경우, 해당 요소가 리턴됩니다.
  • 시 v 스에 요소가 없거나 제공된 predicate 와 일치하는 요소가없는 경우, default(T) 가 리턴됩니다.
  • 시퀀스에 둘 이상의 요소 또는 제공된 predicate 와 일치하는 둘 이상의 요소가 들어 있으면 "시퀀스에 둘 이상의 요소가 포함되어 있습니다"라는 메시지와 함께 InvalidOperationException 이 발생 InvalidOperationException .
  • 시 v 스에 제공된 predicate 와 일치하는 요소가 없으면 default(T) 사용하여 default(T) 유형의 기본값을 리턴합니다.
  • 주 : 순서가 정확하게 1 개의 성분을 포함하고 있는지 평가하기 위하여, 많아야 2 개의 성분은 열거되어야한다.

// Returns "a":
new[] { "a" }.SingleOrDefault();

// returns "a"
new[] { "a", "b" }.SingleOrDefault(x => x == "a"); 

// Returns null:
new[] { "a", "b" }.SingleOrDefault(x => x == "c");

// Throws InvalidOperationException:
new[] { "a", "a" }.SingleOrDefault(x => x == "a");

// Throws InvalidOperationException:
new[] { "a", "b" }.SingleOrDefault();

// Returns null:
new string[0].SingleOrDefault();

권장 사항

  • FirstOrDefault , LastOrDefault 또는 SingleOrDefault 를 사용하여 시퀀스에 항목이 있는지 여부를 확인할 수 있지만 Any 또는 Count 는 더 안정적입니다. 이는 세 가지 메소드 중 하나에서 default(T) 의 리턴 값이 시퀀스의 첫 번째 / 마지막 / 단일 요소의 값이 default(T) 이 동일 할 수 있기 때문에 시퀀스가 ​​비어 있음을 증명하지 않기 때문입니다.

  • 코드의 목적에 가장 적합한 메소드를 결정하십시오. 예를 들어, 술어와 일치하는 콜렉션에 단일 항목이 있는지 확인해야하는 경우에만 Single 사용하십시오. 그렇지 않으면 First 사용하십시오. 시퀀스에 일치하는 요소가 두 개 이상있는 경우 Single 에서 예외를 throw합니다. 이것은 물론 "* OrDefault"- 반대 부분에도 적용됩니다.

  • 효율성에 관해서 : 단 하나의 항목 ( Single ) 또는 하나 또는 0 ( SingleOrDefault ) 항목 중 하나만 쿼리에 의해 반환되도록하는 것이 종종 적합하지만이 두 가지 방법 모두 컬렉션의 더 많은, 그리고 종종 전체를 필요로합니다 쿼리에 대한 두 번째 일치 항목이 없는지 확인합니다. 이것은 첫 번째 일치를 찾은 후에 만족 될 수있는 First 방법과는 다른 행동입니다.

Except 메서드는 첫 번째 컬렉션에는 포함되지만 두 번째 컬렉션에는 포함되지 않은 항목 집합을 반환합니다. 기본 IEqualityComparer 는 두 세트 내의 항목을 비교하는 데 사용됩니다. IEqualityComparer 를 인수로 받아들이는 오버로드가 있습니다.

예:

int[] first = { 1, 2, 3, 4 };
int[] second = { 0, 2, 3, 5 };

IEnumerable<int> inFirstButNotInSecond = first.Except(second);
// inFirstButNotInSecond = { 1, 4 }

산출:

1
4

.NET Fiddle에서의 라이브 데모

이 경우 .Except(second) 배열에 포함되는 원소를 제외 second , 즉 2 및 3 (0, 5가 포함되지 않는 first 배열을 스킵이다).

Except Distinct 의미합니다 (즉, 반복 요소 제거). 예 :

int[] third = { 1, 1, 1, 2, 3, 4 };

IEnumerable<int> inThirdButNotInSecond = third.Except(second);
// inThirdButNotInSecond = { 1, 4 }

산출:

1
4

.NET Fiddle에서의 라이브 데모

이 경우 요소 1과 4는 한 번만 반환됩니다.


IEquatable 구현하거나 함수를 제공하면 IEqualityComparer 는 다른 메소드를 사용하여 요소를 비교할 수 있습니다. GetHashCode 메서드는 IEquatable 구현에 따라 동일한 동일한 해시 코드를 object 가 반환하도록 재정의되어야합니다.

IEquatable의 예 :

class Holiday : IEquatable<Holiday>
{
    public string Name { get; set; }

    public bool Equals(Holiday other)
    {
        return Name == other.Name;
    }

    // GetHashCode must return true whenever Equals returns true.
    public override int GetHashCode()
    {
        //Get hash code for the Name field if it is not null.
        return Name?.GetHashCode() ?? 0;
    }
}

public class Program
{
    public static void Main()
    {
        List<Holiday> holidayDifference = new List<Holiday>();

        List<Holiday> remoteHolidays = new List<Holiday>
        {
            new Holiday { Name = "Xmas" },
            new Holiday { Name = "Hanukkah" },
            new Holiday { Name = "Ramadan" }
        };

        List<Holiday> localHolidays = new List<Holiday>
        {
            new Holiday { Name = "Xmas" },
            new Holiday { Name = "Ramadan" }
        };

        holidayDifference = remoteHolidays
            .Except(localHolidays)
            .ToList();

        holidayDifference.ForEach(x => Console.WriteLine(x.Name));
    }
}

산출:

하누카

.NET Fiddle에서의 라이브 데모

SelectMany : 시퀀스 시퀀스 전개

var sequenceOfSequences = new [] { new [] { 1, 2, 3 }, new [] { 4, 5 }, new [] { 6 } };
var sequence = sequenceOfSequences.SelectMany(x => x);
// returns { 1, 2, 3, 4, 5, 6 }

SelectMany() 사용하거나 시퀀스 시퀀스를 만드는 경우 결과를 하나의 긴 시퀀스로 원할 수 있습니다.

LINQ 쿼리 구문 :

var sequence = from subSequence in sequenceOfSequences
               from item in subSequence
               select item;

컬렉션 모음이 있고 부모 컬렉션과 하위 컬렉션의 데이터를 동시에 작업 할 수 있기를 SelectMany 것도 가능합니다.

간단한 클래스를 정의합시다.

public class BlogPost
{
    public int Id { get; set; }
    public string Content { get; set; }
    public List<Comment> Comments { get; set; }
}

public class Comment
{
    public int Id { get; set; }
    public string Content { get; set; }
}

우리가 다음과 같은 콜렉션을 가지고 있다고 가정 해 봅시다.

List<BlogPost> posts = new List<BlogPost>()
{
    new BlogPost()
    {
        Id = 1,
        Comments = new List<Comment>()
        {
            new Comment()
            {
                Id = 1,
                Content = "It's really great!",
            },
            new Comment()
            {
                Id = 2,
                Content = "Cool post!"
            }
        }
    },
    new BlogPost()
    {
        Id = 2,
        Comments = new List<Comment>()
        {
            new Comment()
            {
                Id = 3,
                Content = "I don't think you're right",
            },
            new Comment()
            {
                Id = 4,
                Content = "This post is a complete nonsense"
            }
        }
    }
};

이제이 댓글과 관련된 BlogPost Id 와 함께 댓글 Content 를 선택하려고합니다. 그렇게하기 위해 적절한 SelectMany 오버로드를 사용할 수 있습니다.

var commentsWithIds = posts.SelectMany(p => p.Comments, (post, comment) => new { PostId = post.Id, CommentContent = comment.Content });

우리 commentsWithIds 은 다음과 같이 보입니다.

{
    PostId = 1,
    CommentContent = "It's really great!"
},
{
    PostId = 1,
    CommentContent = "Cool post!"
},
{
    PostId = 2,
    CommentContent = "I don't think you're right"
},
{
    PostId = 2,
    CommentContent = "This post is a complete nonsense"
}

SelectMany

SelectMany linq 메서드는 IEnumerable<IEnumerable<T>>IEnumerable<T> '담습니다'. 소스 IEnumerable 포함 된 IEnumerable 인스턴스 내의 모든 T 요소는 단일 IEnumerable 로 결합됩니다.

var words = new [] { "a,b,c", "d,e", "f" };
var splitAndCombine = words.SelectMany(x => x.Split(','));
// returns { "a", "b", "c", "d", "e", "f" }

입력 요소를 시퀀스로 바꾸는 선택기 함수를 사용하면 결과는 하나씩 반환되는 시퀀스의 요소가됩니다.

Select() 와 달리 출력의 요소 수는 입력과 동일 할 필요는 없습니다.

보다 실제적인 예

class School
{
    public Student[] Students { get; set; }
}

class Student 
{
    public string Name { get; set; }
}    
  
var schools = new [] {
    new School(){ Students = new [] { new Student { Name="Bob"}, new Student { Name="Jack"} }},
    new School(){ Students = new [] { new Student { Name="Jim"}, new Student { Name="John"} }}
};
               
var allStudents = schools.SelectMany(s=> s.Students);
             
foreach(var student in allStudents)
{
    Console.WriteLine(student.Name);
}

산출:

단발


남자

.NET Fiddle에서의 라이브 데모

모든

All 은 컬렉션의 모든 요소가 조건과 일치하는지 여부를 확인하는 데 사용됩니다.
또한보십시오 : .Any

1. 빈 매개 변수

All : 빈 매개 변수와 함께 사용할 수 없습니다.

2. 파라미터로서의 람다 식

All : 컬렉션의 모든 요소가 람다 식을 만족하면 true 반환하고 그렇지 않으면 false 반환합니다.

var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.All(i => i < 10); // true
bool result = numbers.All(i => i >= 3); // false

3. 빈 컬렉션

모두 : 컬렉션이 비어 있고 람다식이 제공되면 true 반환 true .

var numbers = new List<int>();
bool result = numbers.All(i => i >= 0); // true

참고 : All 는 조건과 일치 하지 않는 요소를 찾자 마자 모음의 반복을 중지합니다. 즉, 컬렉션이 반드시 완전히 열거되지는 않습니다. 조건과 일치하지 않는 첫 번째 항목을 찾을 수있을 정도로만 열거됩니다.

유형별로 검색어 수집 / 입력 할 Cast 요소

interface IFoo { }
class Foo : IFoo { }
class Bar : IFoo { }

var item0 = new Foo();
var item1 = new Foo();
var item2 = new Bar();
var item3 = new Bar();
var collection = new IFoo[] { item0, item1, item2, item3 };

OfType 사용

var foos = collection.OfType<Foo>(); // result: IEnumerable<Foo> with item0 and item1
var bars = collection.OfType<Bar>(); // result: IEnumerable<Bar> item item2 and item3
var foosAndBars = collection.OfType<IFoo>(); // result: IEnumerable<IFoo> with all four items

Where 사용 Where

var foos = collection.Where(item => item is Foo); // result: IEnumerable<IFoo> with item0 and item1
var bars = collection.Where(item => item is Bar); // result: IEnumerable<IFoo> with item2 and item3

Cast 사용

var bars = collection.Cast<Bar>();                // throws InvalidCastException on the 1st item
var foos = collection.Cast<Foo>();                // throws InvalidCastException on the 3rd item
var foosAndBars = collection.Cast<IFoo>();        // OK 

노동 조합

두 개의 컬렉션을 병합하여 기본 동등 비교자를 사용하여 고유 컬렉션을 만듭니다.

int[] numbers1 = { 1, 2, 3 };
int[] numbers2 = { 2, 3, 4, 5 };

var allElement = numbers1.Union(numbers2);   // AllElement now contains 1,2,3,4,5

.NET Fiddle에서의 라이브 데모

가입

조인은 공통 키를 통해 데이터가있는 다른 목록 또는 테이블을 결합하는 데 사용됩니다.

SQL과 마찬가지로 LINQ에서는 다음과 같은 종류의 조인이 지원됩니다.
내부, 왼쪽, 오른쪽, 교차전체 외부 조인.

아래의 두 가지 목록이 아래의 예에서 사용됩니다.

var first = new List<string>(){ "a","b","c"}; // Left data
var second = new List<string>(){ "a", "c", "d"}; // Right data

(내부) 조인

var result = from f in first
             join s in second on f equals s
             select new { f, s };

var result = first.Join(second, 
                        f => f, 
                        s => s,
                        (f, s) => new { f, s });

// Result: {"a","a"}
//         {"c","c"}

왼쪽 외부 조인

var leftOuterJoin = from f in first
                    join s in second on f equals s into temp
                    from t in temp.DefaultIfEmpty()
                    select new { First = f, Second = t};

// Or can also do:
var leftOuterJoin = from f in first
                    from s in second.Where(x => x == f).DefaultIfEmpty()
                    select new { First = f, Second = s};

// Result: {"a","a"}
//         {"b", null}  
//         {"c","c"}  


// Left outer join method syntax
var leftOuterJoinFluentSyntax = first.GroupJoin(second,
                                      f => f,
                                      s => s,
                                      (f, s) => new { First = f, Second = s })
                                   .SelectMany(temp => temp.Second.DefaultIfEmpty(),
                                      (f, s) => new { First = f.First, Second = s });

오른쪽 외부 조인

var rightOuterJoin = from s in second
                     join f in first on s equals f into temp
                     from t in temp.DefaultIfEmpty()
                     select new {First=t,Second=s};

// Result: {"a","a"}
//         {"c","c"}  
//         {null,"d"}  

크로스 조인

var CrossJoin = from f in first
                from s in second
                select new { f, s };

// Result: {"a","a"}
//         {"a","c"}  
//         {"a","d"}  
//         {"b","a"}
//         {"b","c"}  
//         {"b","d"}  
//         {"c","a"}
//         {"c","c"}  
//         {"c","d"}

전체 외부 조인

var fullOuterjoin = leftOuterJoin.Union(rightOuterJoin);

// Result: {"a","a"}
//         {"b", null}  
//         {"c","c"}  
//         {null,"d"}

실용 사례

위의 예제에는 간단한 데이터 구조가 있으므로 기술적으로 다른 LINQ 조인을 이해하는 데 집중할 수 있지만 실제로는 조인 할 열이있는 테이블이 필요합니다.

다음 예제에서는 단 하나의 클래스 Region 사용됩니다. 실제로 동일한 키를 보유하는 두 개 이상의 다른 테이블을 조인합니다 (이 예에서는 firstsecond 가 공통 키 ID 를 통해 조인 됨).

예 : 다음 데이터 구조를 고려하십시오.

public class Region 
{
    public Int32 ID;
    public string RegionDescription;
    
    public Region(Int32 pRegionID, string pRegionDescription=null)
    {
        ID = pRegionID; RegionDescription = pRegionDescription;
    }
}

이제 데이터를 준비하십시오 (즉, 데이터로 채우기).

// Left data
var first = new List<Region>() 
                 { new Region(1), new Region(3), new Region(4) }; 
// Right data
var second = new List<Region>() 
                 { 
                    new Region(1, "Eastern"),  new Region(2, "Western"),
                    new Region(3, "Northern"), new Region(4, "Southern")
                 }; 

이 예제에서는 first 영역 설명이 없으므로 second 영역에서 조인하려고한다는 것을 알 수 있습니다. 그런 다음 내부 조인은 다음과 같습니다.

// do the inner join
var result = from f in first
             join s in second on f.ID equals s.ID
             select new { f.ID, s.RegionDescription };


 // Result: {1,"Eastern"}
 //         {3, Northern}  
 //         {4,"Southern"}  

이 결과는 익명의 객체를 즉석에서 만들었지 만 이미 적절한 클래스를 만들었으므로 지정할 수 있습니다. select new { f.ID, s.RegionDescription }; 대신 select new { f.ID, s.RegionDescription }; 우리는 select new Region(f.ID, s.RegionDescription); 말할 수 있습니다 select new Region(f.ID, s.RegionDescription); 동일한 데이터를 반환하지만 다른 객체와의 호환성을 유지하는 Region 유형의 객체를 만듭니다.

.NET 피들 라이브 데모

뚜렷한

IEnumerable 에서 고유 한 값을 반환합니다. 고유성은 기본 동등 비교자를 사용하여 결정됩니다.

int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };

var distinct = array.Distinct();
// distinct = { 1, 2, 3, 4, 5 }

사용자 지정 데이터 형식을 비교하려면 IEquatable<T> 인터페이스를 구현하고 해당 형식에 대해 GetHashCodeEquals 메서드를 제공해야합니다. 또는 평등 비교자를 재정의 할 수 있습니다.

class SSNEqualityComparer : IEqualityComparer<Person> {
    public bool Equals(Person a, Person b) => return a.SSN == b.SSN;
    public int GetHashCode(Person p) => p.SSN;
}

List<Person> people;

distinct = people.Distinct(SSNEqualityComparer);

GroupBy 하나 이상의 필드

우리가 어떤 영화 모델을 가지고 있다고 가정합시다 :

public class Film {
    public string Title { get; set; }
    public string Category { get; set; }
    public int Year { get; set; }
}

범주 별 그룹 속성 :

foreach (var grp in films.GroupBy(f => f.Category)) {
    var groupCategory = grp.Key;
    var numberOfFilmsInCategory = grp.Count();
}

카테고리 및 연도 별 그룹 :

foreach (var grp in films.GroupBy(f => new { Category = f.Category, Year = f.Year })) {
    var groupCategory = grp.Key.Category;
    var groupYear = grp.Key.Year;
    var numberOfFilmsInCategory = grp.Count();
}

Range를 사용하여 다양한 Linq 메소드 구현하기

Linq 쿼리와 함께 Enumerable 클래스를 사용하여 루프를 Linq 한 개의 라이너로 변환 할 수 있습니다.

예제 선택

이것을하는 것에 반대되는 것 :

var asciiCharacters = new List<char>();
for (var x = 0; x < 256; x++)
{
    asciiCharacters.Add((char)x);
}

당신은 이것을 할 수 있습니다 :

var asciiCharacters = Enumerable.Range(0, 256).Select(a => (char) a);

예제

이 예제에서는 100 개의 숫자가 생성되고 심지어 추출됩니다.

var evenNumbers = Enumerable.Range(1, 100).Where(a => a % 2 == 0);

쿼리 순서 - OrderBy () ThenBy () OrderByDescending () ThenByDescending ()

string[] names= { "mark", "steve", "adam" };

오름차순 :

검색어 구문

var sortedNames =
    from name in names
    orderby name
    select name;

메서드 구문

var sortedNames = names.OrderBy(name => name);

sortedNames는 "adam", "mark", "steve"순서로 이름을 포함합니다.

내림차순 :

검색어 구문

var sortedNames =
    from name in names
    orderby name descending
    select name;

메서드 구문

var sortedNames = names.OrderByDescending(name => name);

sortedNames는 "steve", "mark", "adam"순서로 이름을 포함합니다.

여러 필드로 정렬

Person[] people =
{
    new Person { FirstName = "Steve", LastName = "Collins", Age = 30},
    new Person { FirstName = "Phil" , LastName = "Collins", Age = 28},
    new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 29},
    new Person { FirstName = "Adam" , LastName = "Ackerman", Age = 15}
};

검색어 구문

var sortedPeople = from person in people
                   orderby person.LastName, person.FirstName, person.Age descending
                   select person;

메서드 구문

 sortedPeople = people.OrderBy(person => person.LastName)
                      .ThenBy(person => person.FirstName)
                      .ThenByDescending(person => person.Age);

결과

1. Adam Ackerman 29
2. Adam Ackerman 15
3. Phil Collins  28
4. Steve Collins 30

기초

LINQ는 주로 쿼리 (또는 배열)에 유용합니다.

예를 들어 다음 샘플 데이터가 제공됩니다.

var classroom = new Classroom
{
    new Student { Name = "Alice", Grade = 97, HasSnack = true  },
    new Student { Name = "Bob",   Grade = 82, HasSnack = false },
    new Student { Name = "Jimmy", Grade = 71, HasSnack = true  },
    new Student { Name = "Greg",  Grade = 90, HasSnack = false },
    new Student { Name = "Joe",   Grade = 59, HasSnack = false }
}

LINQ 구문을 사용하여이 데이터를 "쿼리"할 수 있습니다. 예를 들어 오늘 간식을 먹는 모든 학생을 검색하려면 다음과 같이하십시오.

var studentsWithSnacks = from s in classroom.Students
                         where s.HasSnack
                         select s;

또는 90 세 이상의 학생들을 불러오고 전체 Student 객체가 아닌 이름 만 반환하십시오.

var topStudentNames = from s in classroom.Students
                      where s.Grade >= 90
                      select s.Name;

LINQ 기능은 동일한 기능을 수행하고 거의 동일한 성능을 갖지만 전혀 다른 방식으로 작성되는 두 가지 구문으로 구성됩니다. 위 예의 구문은 쿼리 구문 이라고 합니다 . 그러나 다음 예제에서는 메서드 구문을 보여줍니다. 위의 예에서와 동일한 데이터가 반환되지만 쿼리 작성 방법은 다릅니다.

var topStudentNames = classroom.Students
                               .Where(s => s.Grade >= 90)
                               .Select(s => s.Name);

GroupBy

GroupBy는 항목의 IEnumerable<T> 컬렉션을 별개의 그룹으로 쉽게 정렬 할 수 있습니다.

간단한 예

이 첫 번째 예에서는 홀수 항목과 짝수 항목의 두 그룹을 사용합니다.

List<int> iList = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var grouped = iList.GroupBy(x => x % 2 == 0);

//Groups iList into odd [13579] and even[2468] items 
       
foreach(var group in grouped)
{
    foreach (int item in group)
    {
        Console.Write(item); // 135792468  (first odd then even)
    }
}

더 복잡한 예제

연령대별로 사람들의 목록을 예로 들어 봅시다. 먼저 이름과 나이의 두 가지 속성을 가진 Person 객체를 생성합니다.

public class Person
{
    public int Age {get; set;}
    public string Name {get; set;}
}

그런 다음 다양한 이름과 나이를 가진 사람들의 샘플 목록을 만듭니다.

List<Person> people = new List<Person>();
people.Add(new Person{Age = 20, Name = "Mouse"});
people.Add(new Person{Age = 30, Name = "Neo"});
people.Add(new Person{Age = 40, Name = "Morpheus"});
people.Add(new Person{Age = 30, Name = "Trinity"});
people.Add(new Person{Age = 40, Name = "Dozer"});
people.Add(new Person{Age = 40, Name = "Smith"});

그런 다음 LINQ 쿼리를 작성하여 연령대별로 사용자 목록을 그룹화합니다.

var query = people.GroupBy(x => x.Age);

이렇게하면 각 그룹의 나이를 볼 수 있고 그룹의 각 사람의 목록을 볼 수 있습니다.

foreach(var result in query)
{
    Console.WriteLine(result.Key);
                
    foreach(var person in result)
        Console.WriteLine(person.Name);
}

결과는 다음과 같습니다.

20
Mouse
30
Neo
Trinity
40
Morpheus
Dozer
Smith

.NET Fiddle 에서 라이브 데모로 게임을 즐길 수 있습니다.

어떤

Any 컬렉션의 요소가 조건을 일치 여부를 여부를 확인하는 데 사용됩니다.
참고 사항 : .All , Any 및 FirstOrDefault : 모범 사례

1. 빈 매개 변수

Any : 콜렉션에 요소가 있으면 true 반환하고 콜렉션이 비어 있으면 false 반환합니다.

var numbers = new List<int>();
bool result = numbers.Any(); // false

var numbers = new List<int>(){ 1, 2, 3, 4, 5};
bool result = numbers.Any(); //true

2. 파라미터로서의 람다 식

Any : 컬렉션에 람다 식의 조건을 만족하는 요소가 하나 이상있는 경우 true 반환 true .

var arrayOfStrings = new string[] { "a", "b", "c" };
arrayOfStrings.Any(item => item == "a");    // true
arrayOfStrings.Any(item => item == "d");    // false

3. 빈 컬렉션

Any : 컬렉션이 비어 있고 람다 표현식이 제공되면 false 반환합니다.

var numbers = new List<int>();
bool result = numbers.Any(i => i >= 0); // false

주 : Any 이 조건에 일치하는 요소를 발견하는 즉시 컬렉션의 반복을 중지합니다. 즉, 컬렉션이 반드시 완전히 열거되지는 않습니다. 조건과 일치하는 첫 번째 항목을 찾을만큼 충분히 열거됩니다.

.NET Fiddle에서의 라이브 데모

ToDictionary

ToDictionary() LINQ 메서드는 지정된 IEnumerable<T> 원본을 기반으로 Dictionary<TKey, TElement> 컬렉션을 생성하는 데 사용할 수 있습니다.

IEnumerable<User> users = GetUsers();
Dictionary<int, User> usersById = users.ToDictionary(x => x.Id);

이 예제에서 ToDictionary 전달 된 단일 인수는 각 요소의 키를 반환하는 Func<TSource, TKey> 유형입니다.

이것은 다음 작업을 수행하는 간결한 방법입니다.

Dictionary<int, User> usersById = new Dictionary<int User>();
foreach (User u in users) 
{
  usersById.Add(u.Id, u);
}

두 번째 매개 변수를 Func<TSource, TElement> 유형의 ToDictionary 메서드에 전달하고 각 항목에 대해 추가 할 Value 를 반환 할 수도 있습니다.

IEnumerable<User> users = GetUsers();
Dictionary<int, string> userNamesById = users.ToDictionary(x => x.Id, x => x.Name);

키 값을 비교하는 데 사용되는 IComparer 를 지정할 수도 있습니다. 키가 문자열이고 대 / 소문자를 구분하지 않으려는 경우에 유용 할 수 있습니다.

IEnumerable<User> users = GetUsers();
Dictionary<string, User> usersByCaseInsenstiveName = users.ToDictionary(x => x.Name, StringComparer.InvariantCultureIgnoreCase);

var user1 = usersByCaseInsenstiveName["john"];
var user2 = usersByCaseInsenstiveName["JOHN"];
user1 == user2; // Returns true

참고 : ToDictionary 메서드는 모든 키가 고유해야하며 중복 키가 없어야합니다. 있는 경우 예외가 발생합니다. ArgumentException: An item with the same key has already been added. 같은 키를 가진 요소가 여러 개 있다는 것을 알고 있다면 대신 ToLookup 사용하는 것이 좋습니다.

골재

Aggregate 시퀀스에 누적 기 함수를 적용합니다.

int[] intList = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int sum = intList.Aggregate((prevSum, current) => prevSum + current);
// sum = 55
  • 첫 번째 단계에서 prevSum = 1
  • 두 번째 prevSum = prevSum(at the first step) + 2
  • i 번째 단계에서 prevSum = prevSum(at the (i-1) step) + i-th element of the array
string[] stringList = { "Hello", "World", "!" };
string joinedString = stringList.Aggregate((prev, current) => prev + " " + current);
// joinedString = "Hello World !"

또한 Aggregate 의 두 번째 오버로드는 초기 누적 기 값 인 seed 매개 변수를받습니다. 이것은 컬렉션을 여러 번 반복하지 않고 컬렉션의 여러 조건을 계산하는 데 사용할 수 있습니다.

List<int> items = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

계산하려는 items 모음

  1. .Count
  2. 짝수의 양
  3. 각 항목을 모으십시오.

Aggregate 사용하면 다음과 같이 수행 할 수 있습니다.

var result = items.Aggregate(new { Total = 0, Even = 0, FourthItems = new List<int>() },
                (accumelative,item) =>
                new {
                    Total = accumelative.Total + 1,
                    Even = accumelative.Even + (item % 2 == 0 ? 1 : 0),
                    FourthItems = (accumelative.Total + 1)%4 == 0 ? 
                        new List<int>(accumelative.FourthItems) { item } : 
                        accumelative.FourthItems 
                });
// Result:
// Total = 12
// Even = 6
// FourthItems = [4, 8, 12]

익명 형식을 시드로 사용하면 속성이 읽기 전용이므로 각 항목에 새 개체를 인스턴스화해야합니다. 사용자 정의 클래스를 사용하면 간단하게 정보를 할당 할 수 있으며 new 은 필요하지 않습니다 (초기 seed 매개 변수를 제공 할 때만).

Linq 질의 내에서 변수 정의하기 (let 키워드)

linq 표현식에서 변수를 정의하려면 let 키워드를 사용할 수 있습니다. 이것은 대개 중간 하위 쿼리의 결과를 저장하기 위해 수행됩니다. 예를 들면 다음과 같습니다.

 int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

 var aboveAverages = from number in numbers
                     let average = numbers.Average()
                     let nSquared = Math.Pow(number,2)
                     where nSquared > average
                     select number;

 Console.WriteLine("The average of the numbers is {0}.", numbers.Average());

 foreach (int n in aboveAverages)
 {
   Console.WriteLine("Query result includes number {0} with square of {1}.", n, Math.Pow(n,2));
 }

산출:

숫자의 평균은 4.5입니다.
쿼리 결과에는 9의 제곱이있는 숫자 3이 포함됩니다.
쿼리 결과에는 16의 제곱이있는 숫자 4가 포함됩니다.
쿼리 결과에는 25의 제곱이있는 숫자 5가 포함됩니다.
쿼리 결과에는 6의 제곱이있는 숫자 6이 포함됩니다.
쿼리 결과에는 49의 제곱이있는 숫자 7이 포함됩니다.
쿼리 결과에는 제곱이 64 인 숫자 8이 포함됩니다.
쿼리 결과에는 81의 제곱이있는 숫자 9가 포함됩니다.

데모보기

SkipWhile

SkipWhile() 은 첫 번째 SkipWhile() 까지 요소를 제외하는 데 사용됩니다 (대부분 직관에 위배 될 수 있음).

int[] list = { 42, 42, 6, 6, 6, 42 };
var result = list.SkipWhile(i => i == 42); 
// Result: 6, 6, 6, 42

DefaultIfEmpty

DefaultIfEmpty는 시퀀스에 요소가없는 경우 기본 요소를 반환하는 데 사용됩니다. 이 요소는 유형의 기본값이거나 해당 유형의 사용자 정의 인스턴스 일 수 있습니다. 예:

var chars = new List<string>() { "a", "b", "c", "d" };

chars.DefaultIfEmpty("N/A").FirstOrDefault(); // returns "a";

chars.Where(str => str.Length > 1)
     .DefaultIfEmpty("N/A").FirstOrDefault(); // return "N/A"

chars.Where(str => str.Length > 1)
        .DefaultIfEmpty().First(); // returns null;

왼쪽 조인의 사용법 :

DefaultIfEmpty 하면 전통적인 Linq Join은 일치하는 것이 없을 경우 기본 객체를 반환 할 수 있습니다. 따라서 SQL의 왼쪽 결합으로 작동합니다. 예:

var leftSequence = new List<int>() { 99, 100, 5, 20, 102, 105 };
var rightSequence = new List<char>() { 'a', 'b', 'c', 'i', 'd' };

var numbersAsChars = from l in leftSequence
                     join r in rightSequence
                     on l equals (int)r into leftJoin
                     from result in leftJoin.DefaultIfEmpty('?')
                     select new
                     {
                         Number = l,
                         Character = result
                     };

foreach(var item in numbersAsChars)
{
    Console.WriteLine("Num = {0} ** Char = {1}", item.Number, item.Character);
}

ouput: 

Num = 99         Char = c
Num = 100        Char = d
Num = 5          Char = ?
Num = 20         Char = ?
Num = 102        Char = ?
Num = 105        Char = i

DefaultIfEmpty 가 사용되는 경우 (기본값을 지정하지 않고) 올바른 순서로 일치하는 항목이 없으면 해당 속성에 액세스하기 전에 해당 객체가 null 이 아닌지 확인해야합니다. 그렇지 않으면 NullReferenceException 이 발생합니다. 예:

var leftSequence = new List<int> { 1, 2, 5 };
var rightSequence = new List<dynamic>()
    {
        new { Value = 1 },
        new { Value = 2 },
        new { Value = 3 },
        new { Value = 4 },
    };

var numbersAsChars = (from l in leftSequence
                        join r in rightSequence
                        on l equals r.Value into leftJoin
                        from result in leftJoin.DefaultIfEmpty()
                        select new
                        {
                            Left = l,
                            // 5 will not have a matching object in the right so result 
                            // will be equal to null. 
                            // To avoid an error use:
                            //    -  C# 6.0 or above - ?. 
                            //    -  Under           - result == null ? 0 : result.Value
                            Right = result?.Value
                        }).ToList();

SequenceEqual

SequenceEqual 은 두 개의 IEnumerable<T> 시퀀스를 서로 비교하는 데 사용됩니다.

int[] a = new int[] {1, 2, 3};
int[] b = new int[] {1, 2, 3};
int[] c = new int[] {1, 3, 2};

bool returnsTrue = a.SequenceEqual(b);
bool returnsFalse = a.SequenceEqual(c);

카운트와 롱 카운트

CountIEnumerable<T> 의 요소 Count 반환합니다. Count 는 또한 계산할 요소를 필터링 할 수있는 선택적 술어 매개 변수를 제공합니다.

int[] array = { 1, 2, 3, 4, 2, 5, 3, 1, 2 };

int n = array.Count(); // returns the number of elements in the array
int x = array.Count(i => i > 2); // returns the number of elements in the array greater than 2

LongCountCount 와 같은 방식으로 작동하지만 반환 유형이 long 이며 int.MaxValue 보다 긴 IEnumerable<T> 시퀀스를 계산하는 데 사용됩니다.

int[] array = GetLargeArray();

long n = array.LongCount(); // returns the number of elements in the array
long x = array.LongCount(i => i > 100); // returns the number of elements in the array greater than 100

점진적으로 쿼리 작성

LINQ는 지연된 실행을 사용하기 때문에 실제로 값을 포함하지 않지만 평가할 때 값을 반환하는 쿼리 개체를 가질 수 있습니다. 따라서 우리는 제어 흐름을 기반으로 동적으로 쿼리를 작성하고 완료되면 평가할 수 있습니다.

IEnumerable<VehicleModel> BuildQuery(int vehicleType, SearchModel search, int start = 1, int count = -1) {
    IEnumerable<VehicleModel> query = _entities.Vehicles
        .Where(x => x.Active && x.Type == vehicleType)
        .Select(x => new VehicleModel {
            Id = v.Id,
            Year = v.Year,
            Class = v.Class,
            Make = v.Make,
            Model = v.Model,
            Cylinders = v.Cylinders ?? 0
        });

조건부로 필터를 적용 할 수 있습니다.

    if (!search.Years.Contains("all", StringComparer.OrdinalIgnoreCase))
        query = query.Where(v => search.Years.Contains(v.Year));

    if (!search.Makes.Contains("all", StringComparer.OrdinalIgnoreCase)) {
        query = query.Where(v => search.Makes.Contains(v.Make));
    }

    if (!search.Models.Contains("all", StringComparer.OrdinalIgnoreCase)) {
        query = query.Where(v => search.Models.Contains(v.Model));
    }

    if (!search.Cylinders.Equals("all", StringComparer.OrdinalIgnoreCase)) {
        decimal minCylinders = 0;
        decimal maxCylinders = 0;
        switch (search.Cylinders) {
            case "2-4":
                maxCylinders = 4;
                break;
            case "5-6":
                minCylinders = 5;
                maxCylinders = 6;
                break;
            case "8":
                minCylinders = 8;
                maxCylinders = 8;
                break;
            case "10+":
                minCylinders = 10;
                break;
        }
        if (minCylinders > 0) {
            query = query.Where(v => v.Cylinders >= minCylinders);
        }
        if (maxCylinders > 0) {
            query = query.Where(v => v.Cylinders <= maxCylinders);
        }
    }

조건에 따라 쿼리에 정렬 순서를 추가 할 수 있습니다.

    switch (search.SortingColumn.ToLower()) {
        case "make_model":
            query = query.OrderBy(v => v.Make).ThenBy(v => v.Model);
            break;
        case "year":
            query = query.OrderBy(v => v.Year);
            break;
        case "engine_size":
            query = query.OrderBy(v => v.EngineSize).ThenBy(v => v.Cylinders);
            break;
        default:
            query = query.OrderBy(v => v.Year); //The default sorting.
    }

우리의 질의는 주어진 지점에서 시작하도록 정의 될 수 있습니다 :

    query = query.Skip(start - 1);

특정 수의 레코드를 반환하도록 정의됩니다.

    if (count > -1) {
        query = query.Take(count);
    }
    return query;
}

쿼리 개체가 있으면 foreach 루프 또는 ToList 또는 ToArray 와 같은 값 집합을 반환하는 LINQ 메서드 중 하나를 사용하여 결과를 평가할 수 있습니다.

SearchModel sm;

// populate the search model here
// ...

List<VehicleModel> list = BuildQuery(5, sm).ToList();

지퍼

Zip 확장 메소드는 두 개의 콜렉션에서 작동합니다. 그것은 위치에 따라 두 시리즈의 각 요소를 함께 연결합니다. Func 인스턴스의 경우 Zip 을 사용하여 두 C # 컬렉션의 요소를 쌍으로 처리합니다. 시리즈의 크기가 다른 경우 큰 시리즈의 추가 요소는 무시됩니다.

책 "C # in a Nutshell"의 예를 보려면,

int[] numbers = { 3, 5, 7 };
string[] words = { "three", "five", "seven", "ignored" };
IEnumerable<string> zip = numbers.Zip(words, (n, w) => n + "=" + w);

산출:

3 = 3
5 = 5
7 = 7

데모보기

바깥 쪽 범위 변수가있는 GroupJoin

Customer[] customers = Customers.ToArray();
Purchase[] purchases = Purchases.ToArray();

var groupJoinQuery =
    from c in customers
    join p in purchases on c.ID equals p.CustomerID
    into custPurchases
    select new
    {
        CustName = c.Name,
        custPurchases
    };

ElementAt 및 ElementAtOrDefault

ElementAt 는 인덱스 n 의 항목을 반환합니다. n 이 열거 가능 범위 내에 있지 않으면 ArgumentOutOfRangeException 시킵니다.

int[] numbers  = { 1, 2, 3, 4, 5 };
numbers.ElementAt(2);  // 3
numbers.ElementAt(10); // throws ArgumentOutOfRangeException

ElementAtOrDefault 는 인덱스 n 의 항목을 반환합니다. n 가 열거 가능 범위 내에 있지 않으면 default(T) 반환합니다.

int[] numbers  = { 1, 2, 3, 4, 5 };
numbers.ElementAtOrDefault(2);  // 3
numbers.ElementAtOrDefault(10); // 0 = default(int)

ElementAtElementAtOrDefault 는 소스가 IList<T> 일 때 최적화되어 있으며 이러한 경우에는 일반적인 인덱싱이 사용됩니다.

ElementAt 의 경우 제공된 인덱스가 IList<T> 의 크기보다 큰 경우 목록에서 ArgumentOutOfRangeException throw해야합니다 (기술적으로 보장 할 수는 없음).

Linq 수량 한정자

수량 연산자는 시퀀스의 일부 또는 모든 요소가 조건을 만족하면 부울 값을 반환합니다. 이 기사에서는 이러한 연산자를 사용할 수있는 몇 가지 일반적인 LINQ to Objects 시나리오를 살펴 보겠습니다. LINQ에서 사용할 수있는 3 개의 한정자 연산이 있습니다.

All - 시퀀스의 모든 요소가 조건을 만족하는지 여부를 결정하는 데 사용됩니다. 예 :

int[] array = { 10, 20, 30 }; 
   
// Are all elements >= 10? YES
array.All(element => element >= 10); 
   
// Are all elements >= 20? NO
array.All(element => element >= 20);
    
// Are all elements < 40? YES
array.All(element => element < 40);

Any - 순서의 ​​요소가 조건을 만족시키는 지 어떤지를 판정하기 위해서 사용됩니다. 예 :

int[] query=new int[] { 2, 3, 4 }
query.Any (n => n == 3);

Contains - 시퀀스에 지정된 요소가 포함되어 있는지 여부를 확인하는 데 사용됩니다. 예 :

//for int array
int[] query =new int[] { 1,2,3 };
query.Contains(1);

//for string array
string[] query={"Tom","grey"};
query.Contains("Tom");

//for a string
var stringValue="hello";
stringValue.Contains("h");

여러 시퀀스 결합

Customer , PurchasePurchaseItem 엔티티를 다음과 같이 고려하십시오.

public class Customer
{
   public string Id { get; set } // A unique Id that identifies customer    
   public string Name  {get; set; }
}

public class Purchase
{
   public string Id { get; set }
   public string CustomerId {get; set; }
   public string Description { get; set; }
}

public class PurchaseItem
{
   public string Id { get; set }
   public string PurchaseId {get; set; }
   public string Detail { get; set; }
}

위 엔티티에 대한 샘플 데이터를 다음과 같이 고려하십시오.

var customers = new List<Customer>()             
 {
    new Customer() {
        Id = Guid.NewGuid().ToString(),
        Name = "Customer1"            
    },
            
    new Customer() {
        Id = Guid.NewGuid().ToString(),
        Name = "Customer2"            
    }
 };        
    
 var purchases = new List<Purchase>() 
 {
     new Purchase() {                
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[0].Id,
         Description = "Customer1-Purchase1"            
     },

     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[0].Id,
         Description = "Customer1-Purchase2"            
     },
     
     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[1].Id,
         Description = "Customer2-Purchase1"            
     },

     new Purchase() {
         Id = Guid.NewGuid().ToString(),
         CustomerId = customers[1].Id,
         Description = "Customer2-Purchase2"            
     }
  };
    
 var purchaseItems = new List<PurchaseItem>() 
 {
     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[0].Id,
         Detail = "Purchase1-PurchaseItem1"            
     },

     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[1].Id,
         Detail = "Purchase2-PurchaseItem1"            
     },
     
     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[1].Id,
         Detail = "Purchase2-PurchaseItem2"            
     },

     new PurchaseItem() {                
         Id = Guid.NewGuid().ToString(),
         PurchaseId= purchases[3].Id,
         Detail = "Purchase3-PurchaseItem1"
     }
 };

자, linq 쿼리를 고려하십시오 :

var result = from c in customers
            join p in purchases on c.Id equals p.CustomerId           // first join
            join pi in purchaseItems on p.Id equals pi.PurchaseId     // second join
            select new
            {
               c.Name, p.Description, pi.Detail
            };

위 쿼리의 결과를 출력하려면 :

foreach(var resultItem in result)
{
    Console.WriteLine($"{resultItem.Name}, {resultItem.Description}, {resultItem.Detail}");
}

쿼리 출력은 다음과 같습니다.

고객 1, 고객 1 - 구매 1, 구매 1 - 구매 항목 1

고객 1, 고객 1 - 구매 2, 구매 2 - 구매 항목 1

고객 1, 고객 1 - 구매 2, 구매 2 - 구매 항목 2

고객 2, 고객 2 - 구매 2, 구매 3 - 구매 항목 1

.NET Fiddle에서의 라이브 데모

여러 개의 키에 조인하기

  PropertyInfo[] stringProps = typeof (string).GetProperties();//string properties
  PropertyInfo[] builderProps = typeof(StringBuilder).GetProperties();//stringbuilder properties
    
    var query =
        from s in stringProps
        join b in builderProps
            on new { s.Name, s.PropertyType } equals new { b.Name, b.PropertyType }
        select new
        {
            s.Name,
            s.PropertyType,
            StringToken = s.MetadataToken,
            StringBuilderToken = b.MetadataToken
        };

위의 join 에서 익명 형식은 모든 속성이 동일한 경우에만 개체가 동등한 것으로 간주되므로 동일한 속성을 포함해야합니다. 그렇지 않으면 쿼리가 컴파일되지 않습니다.

Func로 선택하십시오. selector - 요소의 순위를 얻는 데 사용합니다.

의 오버로드의에서 Select 확장 방법도 통과 index 되는 컬렉션의 현재 항목의 select 에드. 이것은 그것의 약간 용도이다.

항목의 "행 번호"가져 오기

var rowNumbers = collection.OrderBy(item => item.Property1)
                           .ThenBy(item => item.Property2)
                           .ThenByDescending(item => item.Property3)
                           .Select((item, index) => new { Item = item, RowNumber = index })
                           .ToList();

그룹 항목의 순위를 가져옵니다.

var rankInGroup = collection.GroupBy(item => item.Property1)
                            .OrderBy(group => group.Key)
                            .SelectMany(group => group.OrderBy(item => item.Property2)
                                                   .ThenByDescending(item => item.Property3)
                                                   .Select((item, index) => new 
                                                   { 
                                                       Item = item, 
                                                       RankInGroup = index 
                                                   })).ToList();

그룹 순위 (Oracle에서 dense_rank라고도 함)를 얻습니다.

var rankOfBelongingGroup = collection.GroupBy(item => item.Property1)
                            .OrderBy(group => group.Key)
                            .Select((group, index) => new
                            {
                                Items = group,
                                Rank = index
                            })
                            .SelectMany(v => v.Items, (s, i) => new
                            {
                                Item = i,
                                DenseRank = s.Rank
                            }).ToList();

이것을 테스트하려면 다음을 사용할 수 있습니다.

public class SomeObject
{
    public int Property1 { get; set; }
    public int Property2 { get; set; }
    public int Property3 { get; set; }

    public override string ToString()
    {
        return string.Join(", ", Property1, Property2, Property3);
    }
}

그리고 데이터 :

List<SomeObject> collection = new List<SomeObject>
{
    new SomeObject { Property1 = 1, Property2 = 1, Property3 = 1},
    new SomeObject { Property1 = 1, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 1, Property2 = 2, Property3 = 2},
    new SomeObject { Property1 = 2, Property2 = 1, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 2, Property3 = 1},
    new SomeObject { Property1 = 2, Property2 = 3, Property3 = 1}
};

TakeWhile

TakeWhile 은 조건이 참인 동안 시퀀스에서 요소를 반환합니다.

int[] list = { 1, 10, 40, 50, 44, 70, 4 };
var result = list.TakeWhile(item => item < 50).ToList();
// result = { 1, 10, 40 }

합집합

Enumerable.Sum 확장 메서드는 숫자 값의 합계를 계산합니다.

컬렉션의 요소 자체가 숫자 인 경우 합계를 직접 계산할 수 있습니다.

int[] numbers = new int[] { 1, 4, 6 };
Console.WriteLine( numbers.Sum() ); //outputs 11

요소 유형이 복합 유형 인 경우 람다 식을 사용하여 계산해야하는 값을 지정할 수 있습니다.

var totalMonthlySalary = employees.Sum( employee => employee.MonthlySalary );

Sum 확장 메소드는 다음 유형으로 계산할 수 있습니다.

  • Int32
  • Int64
  • 단일
  • 더블
  • 소수

컬렉션에 nullable 유형이 포함 된 경우 null-coalescing 연산자를 사용하여 null 요소의 기본값을 설정할 수 있습니다.

int?[] numbers = new int?[] { 1, null, 6 };
Console.WriteLine( numbers.Sum( number => number ?? 0 ) ); //outputs 7

찾아보다

ToLookup은 인덱싱을 허용하는 데이터 구조를 반환합니다. 확장 방법입니다. foreach 루프를 사용하여 색인을 생성하거나 열거 할 수있는 ILookup 인스턴스를 생성합니다. 항목은 각 키의 그룹으로 결합됩니다. - dotnetperls

string[] array = { "one", "two", "three" };
//create lookup using string length as key
var lookup = array.ToLookup(item => item.Length);

//join the values whose lengths are 3
Console.WriteLine(string.Join(",",lookup[3]));
//output: one,two

다른 예시:

int[] array = { 1,2,3,4,5,6,7,8 };
//generate lookup for odd even numbers (keys will be 0 and 1)
var lookup = array.ToLookup(item => item % 2);

//print even numbers after joining
Console.WriteLine(string.Join(",",lookup[0]));
//output: 2,4,6,8

//print odd numbers after joining
Console.WriteLine(string.Join(",",lookup[1]));
//output: 1,3,5,7

IEnumerable에 대한 자신의 Linq 연산자를 작성하십시오.

Linq의 가장 큰 장점 중 하나는 확장하기가 쉽다는 것입니다. 인수가 IEnumerable<T>확장 메서드 를 만들어야합니다.

public namespace MyNamespace
{
    public static class LinqExtensions
    {
        public static IEnumerable<List<T>> Batch<T>(this IEnumerable<T> source, int batchSize)
        {
            var batch = new List<T>();
            foreach (T item in source)
            {
                batch.Add(item);
                if (batch.Count == batchSize)
                {
                    yield return batch;
                    batch = new List<T>();
                }
            }
            if (batch.Count > 0)
                yield return batch;
        }
    }
}

이 예제에서는 IEnumerable<T> 의 항목을 나머지 항목이 포함 된 마지막 목록 인 고정 크기 목록으로 분할합니다. this 키워드를 사용하여 확장 메소드가 적용된 객체가 초기 인수로 (인수 source ) 전달되는 방법에 주목하십시오. 그런 다음 yield 키워드는 그 지점에서 실행을 계속하기 전에 출력 IEnumerable<T> 의 다음 항목을 출력하는 데 사용됩니다 ( yield 키워드 참조).

이 예제는 다음과 같이 코드에서 사용됩니다.

//using MyNamespace;
var items = new List<int> { 2, 3, 4, 5, 6 };
foreach (List<int> sublist in items.Batch(3))
{
    // do something
}

첫 번째 루프에서 하위 목록은 {2, 3, 4} 이고 두 번째 {5, 6} 있습니다.

사용자 정의 LinQ 메소드는 표준 LinQ 메소드와도 결합 할 수 있습니다. 예 :

//using MyNamespace;
var result = Enumerable.Range(0, 13)         // generate a list
                       .Where(x => x%2 == 0) // filter the list or do something other
                       .Batch(3)             // call our extension method
                       .ToList()             // call other standard methods

이 쿼리는 크기가 {0, 2, 4}, {6, 8, 10}, {12} 배치로 그룹화 된 짝수를 반환합니다.

using MyNamespace; 를 사용해야한다는 것을 기억하십시오 using MyNamespace; 확장 메서드에 액세스 할 수 있도록

중첩 루프 대신 SelectMany 사용

주어진 2 목록

var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "1", "2", "3", "4" };

모든 순열을 출력하려면 중첩 루프를 사용할 수 있습니다.

var result = new List<string>();
foreach (var s1 in list1)
    foreach (var s2 in list2)
        result.Add($"{s1}{s2}");

SelectMany를 사용하면 다음과 같은 작업을 수행 할 수 있습니다.

var result = list1.SelectMany(x => list2.Select(y => $"{x}{y}", x, y)).ToList();

Any and First (OrDefault) - 모범 사례

AnyFirstOrDefault 는 이미 두 가지 좋은 예가 있으므로 설명하지 FirstOrDefault 습니다. 자세한 내용은 AnyFirst, FirstOrDefault, Last, LastOrDefault, Single 및 SingleOrDefault 를 참조하십시오.

코드에서 자주보아야하는 패턴은 피해야합니다.

if (myEnumerable.Any(t=>t.Foo == "Bob"))
{
    var myFoo = myEnumerable.First(t=>t.Foo == "Bob");
    //Do stuff
}

이처럼 더 효율적으로 작성 될 수 있습니다.

var myFoo = myEnumerable.FirstOrDefault(t=>t.Foo == "Bob");
if (myFoo != null)
{
    //Do stuff
}

두 번째 예제를 사용하면 컬렉션이 한 번만 검색되고 첫 번째 예제와 동일한 결과가 제공됩니다. Single 아이디어에도 동일한 아이디어가 적용될 수 있습니다.

GroupBy 합계 및 개수

샘플 수업을 봅시다.

public class Transaction
{
    public string Category { get; set; }
    public DateTime Date { get; set; }
    public decimal Amount { get; set; }
}

이제 트랜잭션 목록을 살펴 보겠습니다.

var transactions = new List<Transaction>
{
   new Transaction { Category = "Saving Account", Amount = 56, Date = DateTime.Today.AddDays(1) },
   new Transaction { Category = "Saving Account", Amount = 10, Date = DateTime.Today.AddDays(-10) },
   new Transaction { Category = "Credit Card", Amount = 15, Date = DateTime.Today.AddDays(1) },
   new Transaction { Category = "Credit Card", Amount = 56, Date = DateTime.Today },
   new Transaction { Category = "Current Account", Amount = 100, Date = DateTime.Today.AddDays(5) },
};

총계와 개수의 카테고리 현명한 합계를 계산하려면 다음과 같이 GroupBy를 사용할 수 있습니다.

var summaryApproach1 = transactions.GroupBy(t => t.Category)
                           .Select(t => new
                           {
                               Category = t.Key,
                               Count = t.Count(),
                               Amount = t.Sum(ta => ta.Amount),
                           }).ToList();

Console.WriteLine("-- Summary: Approach 1 --");
summaryApproach1.ForEach(
            row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));

또는 한 단계에서이 작업을 수행 할 수 있습니다.

var summaryApproach2 = transactions.GroupBy(t => t.Category, (key, t) =>
{
        var transactionArray = t as Transaction[] ?? t.ToArray();
        return new
        {
            Category = key,
            Count = transactionArray.Length,
            Amount = transactionArray.Sum(ta => ta.Amount),
        };
}).ToList();

Console.WriteLine("-- Summary: Approach 2 --");
summaryApproach2.ForEach(
row => Console.WriteLine($"Category: {row.Category}, Amount: {row.Amount}, Count: {row.Count}"));

위의 두 쿼리에 대한 출력은 동일합니다.

카테고리 : 저축 예금, 금액 : 66, 개수 : 2

카테고리 : 신용 카드, 금액 : 71, 개수 : 2

카테고리 : 당좌 계정, 금액 : 100, 개수 : 1

.NET Fiddle의 라이브 데모

  • 시퀀스의 요소 순서를 반전합니다.
  • 항목이없는 경우 ArgumentNullException: source is null.

예:

// Create an array.
int[] array = { 1, 2, 3, 4 };                         //Output:
// Call reverse extension method on the array.        //4
var reverse = array.Reverse();                        //3
// Write contents of array to screen.                 //2
foreach (int value in reverse)                        //1
    Console.WriteLine(value);

라이브 코드 예제

Remeber는 Reverse() 가 LINQ 문의 연쇄 순서에 따라 다르게 작동 할 수 있습니다.

        //Create List of chars
        List<int> integerlist = new List<int>() { 1, 2, 3, 4, 5, 6 };

        //Reversing the list then taking the two first elements
        IEnumerable<int> reverseFirst = integerlist.Reverse<int>().Take(2);
        
        //Taking 2 elements and then reversing only thos two
        IEnumerable<int> reverseLast = integerlist.Take(2).Reverse();
        
        //reverseFirst output: 6, 5
        //reverseLast output:  2, 1

라이브 코드 예제

Reverse () 는 모든 것을 버퍼링하여 거꾸로 처리합니다. 매우 효율적이지는 않지만 그 관점에서 OrderBy도 아닙니다.

LINQ-to-Objects에는 버퍼링 작업 (Reverse, OrderBy, GroupBy 등) 및 비 버퍼링 작업 (Where, Take, Skip 등)이 있습니다.

예 : 비 버퍼링 역방향 확장

public static IEnumerable<T> Reverse<T>(this IList<T> list) {
    for (int i = list.Count - 1; i >= 0; i--) 
        yield return list[i];
}

라이브 코드 예제

이 메서드는 반복 중에 목록을 변경하면 문제가 발생할 수 있습니다.

열거 형의 열거

IEnumerable <T> 인터페이스는 모든 일반 열거 자에 대한 기본 인터페이스이며 LINQ를 이해하는 데있어 중요한 부분입니다. 핵심은 시퀀스를 나타냅니다.

이 기본 인터페이스는 Collection <T> , Array , List <T> , Dictionary <TKey, TValue> 클래스HashSet <T> 와 같은 모든 일반 컬렉션에 상속됩니다.

순서를 나타내는 것 외에도 IEnumerable <T>에서 상속하는 클래스는 IEnumerator <T>를 제공해야합니다. 열거자는이 열거자를 열거 형에 대해 노출하고이 두 개의 상호 연결된 인터페이스와 아이디어는 "열거 형 열거 형"열거의 원천입니다.

"열거 형을 열거하는 것"은 중요한 문구입니다. 열거 형은 단순히 반복하는 방법에 대한 구조이며 구체화 된 객체를 보유하지 않습니다. 예를 들어 정렬 할 때 열거 형은 정렬 할 필드 기준을 보유 할 수 있지만 .OrderBy() 를 사용하면 정렬 방법 만 알고있는 IEnumerable <T>가 반환됩니다. 객체를 구체화 할 호출을 사용하면 집합을 반복 할 때와 같이 열거 형 (예 : .ToList() )이라고합니다. 열거 프로세스는 일련의 이동과 관련 오브젝트를 반환하기 위해 어떻게 상기 열거 정의 (위하여 여과 예상 등)를 사용한다.

열거 형이 열거 된 후에 만 ​​개체의 구체화가 발생합니다. 시간 복잡성 (시리즈 크기와 관련된 소요 시간 ) 및 공간 복잡성 (시리즈 크기와 관련하여 사용되는 공간)과 같은 통계가 측정해라.

IEnumerable <T>로부터 상속받은 자신의 클래스를 생성하는 것은 열거 할 수있는 기본 시리즈에 따라 약간 복잡 할 수 있습니다. 일반적으로 기존 제네릭 컬렉션 중 하나를 사용하는 것이 가장 좋습니다. 즉, 정의 된 배열을 기본 구조로 가지지 않고도 IEnumerable <T> 인터페이스를 상속하는 것이 가능합니다.

예를 들어 피보나치 시리즈를 기본 시퀀스로 사용합니다. Where 대한 호출은 단순히 IEnumerable 작성하며, 값을 구체화하는 열거 형을 열거하도록 호출 할 때가 아닙니다.

void Main()
{
    Fibonacci Fibo = new Fibonacci();
    IEnumerable<long> quadrillionplus = Fibo.Where(i => i > 1000000000000);
    Console.WriteLine("Enumerable built");
    Console.WriteLine(quadrillionplus.Take(2).Sum());
    Console.WriteLine(quadrillionplus.Skip(2).First());

    IEnumerable<long> fibMod612 = Fibo.OrderBy(i => i % 612);
    Console.WriteLine("Enumerable built");
    Console.WriteLine(fibMod612.First());//smallest divisible by 612
}

public class Fibonacci : IEnumerable<long>
{
    private int max = 90;

    //Enumerator called typically from foreach
    public IEnumerator GetEnumerator() {
        long n0 = 1;
        long n1 = 1;
        Console.WriteLine("Enumerating the Enumerable");
        for(int i = 0; i < max; i++){
            yield return n0+n1;
            n1 += n0;
            n0 = n1-n0;
        }
    }
    
    //Enumerable called typically from linq
    IEnumerator<long> IEnumerable<long>.GetEnumerator() {
        long n0 = 1;
        long n1 = 1;
        Console.WriteLine("Enumerating the Enumerable");
        for(int i = 0; i < max; i++){
            yield return n0+n1;
            n1 += n0;
            n0 = n1-n0;
        }
    }
}

산출

Enumerable built
Enumerating the Enumerable
4052739537881
Enumerating the Enumerable
4052739537881
Enumerable built
Enumerating the Enumerable
14930352

두 번째 집합 (fibMod612)의 강도는 피보나치 수 집합 전체를 순서대로 호출했지만, .First .First() 사용하여 하나의 값만 가져 왔기 때문에 시간 복잡도는 O (n)이 1 값 이었기 때문에 순서 알고리즘의 실행 중에 비교 될 필요가 있었다. 이것은 우리 열거자가 1 개의 값만을 요구했기 때문에 전체 열거 형을 구체화 할 필요가 없었기 때문입니다. 우리가 사용했다 .Take(5) 대신 .First() 열거 5 개 값을 요구 한 것이고, 최대 5 개 값이 구체화 될 필요가있을 것이다. 전체 세트를 주문한 첫 번째 5 개 값 취하는 것과 비교할 때, 실행의 시간과 공간이 많이 절약됩니다.

주문

컬렉션을 지정된 값으로 정렬합니다.

값이 정수 , double 또는 float 일 때 최소 값 부터 시작합니다. 즉, 0부터 시작하여 음수 값보다 먼저 음수 값을 얻습니다 (예제 1 참조).

char에 의해 명령을 내릴 때이 메소드는 chars의 ascii 값 을 비교하여 콜렉션을 정렬합니다 (예제 2 참조).

문자열 을 정렬 할 때 OrderBy 메서드는 CultureInfo를 살펴봄으로써 알파벳을 비교하지만 알파벳 (a, b, c ...)의 첫 글자 로 시작합니다.

이런 종류의 순서를 오름차순이라고 부릅니다. 원한다면 내림차순으로 처리해야합니다 (OrderByDescending 참조).

예제 1 :

int[] numbers = {2, 1, 0, -1, -2};
IEnumerable<int> ascending = numbers.OrderBy(x => x);
// returns {-2, -1, 0, 1, 2}

예 2 :

 char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
 IEnumerable<char> ascending = letters.OrderBy(x => x);
 // returns { ' ', '!', '+', '1', '9', '?', 'A', 'B', 'Y', 'Z', '[', 'a', 'b', 'y', 'z', '{' }

예:

class Person
{
   public string Name { get; set; }
   public int Age { get; set; }
}

var people = new[]
{
    new Person {Name = "Alice", Age = 25},
    new Person {Name = "Bob", Age = 21},
    new Person {Name = "Carol", Age = 43}
};
var youngestPerson = people.OrderBy(x => x.Age).First();
var name = youngestPerson.Name; // Bob

OrderByDescending

컬렉션을 지정된 값으로 정렬합니다.

값이 정수 , double 또는 float 일 때 최대 값 부터 시작합니다. 즉, 0부터 시작하여 음수 값보다 큰 양수 값을 얻습니다 (예제 1 참조).

char에 의해 명령을 내릴 때이 메소드는 chars의 ascii 값 을 비교하여 콜렉션을 정렬합니다 (예제 2 참조).

문자열 을 정렬 할 때 OrderBy 메서드는 CultureInfo를 살펴보고 알파벳 (z, y, x, ...)의 마지막 문자 로 시작하는 순서로 비교합니다.

이런 종류의 순서를 내림차순이라고 부릅니다. 원한다면 오름차순으로 정렬해야합니다 (OrderBy 참조).

예제 1 :

int[] numbers = {-2, -1, 0, 1, 2};
IEnumerable<int> descending = numbers.OrderByDescending(x => x);
// returns {2, 1, 0, -1, -2}

예 2 :

char[] letters = {' ', '!', '?', '[', '{', '+', '1', '9', 'a', 'A', 'b', 'B', 'y', 'Y', 'z', 'Z'};
IEnumerable<char> descending = letters.OrderByDescending(x => x);
// returns { '{', 'z', 'y', 'b', 'a', '[', 'Z', 'Y', 'B', 'A', '?', '9', '1', '+', '!', ' ' }

예 3 :

class Person
{
   public  string Name { get; set; }
   public  int Age { get; set; }
}

var people = new[]
{
    new Person {Name = "Alice", Age = 25},
    new Person {Name = "Bob", Age = 21},
    new Person {Name = "Carol", Age = 43}
};
var oldestPerson = people.OrderByDescending(x => x.Age).First();
var name = oldestPerson.Name; // Carol

콩 카트

두 개의 컬렉션을 병합합니다 (중복을 제거하지 않음).

List<int> foo = new List<int> { 1, 2, 3 };
List<int> bar = new List<int> { 3, 4, 5 };

// Through Enumerable static class
var result = Enumerable.Concat(foo, bar).ToList(); // 1,2,3,3,4,5

// Through extension method
var result = foo.Concat(bar).ToList(); // 1,2,3,3,4,5

내용

MSDN :

지정된 IEqualityComparer<T> 사용하여 시퀀스에 지정된 요소가 들어 있는지 여부를 확인합니다.

List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
var result1 = numbers.Contains(4); // true
var result2 = numbers.Contains(8); // false

List<int> secondNumberCollection = new List<int> { 4, 5, 6, 7 };
// Note that can use the Intersect method in this case
var result3 = secondNumberCollection.Where(item => numbers.Contains(item)); // will be true only for 4,5

사용자 정의 객체 사용 :

public class Person
{
   public string Name { get; set; }
}

List<Person> objects = new List<Person>
{
    new Person { Name = "Nikki"},
    new Person { Name = "Gilad"},
    new Person { Name = "Phil"},
    new Person { Name = "John"}
};

//Using the Person's Equals method - override Equals() and GetHashCode() - otherwise it
//will compare by reference and result will be false
var result4 = objects.Contains(new Person { Name = "Phil" }); // true

Enumerable.Contains(value, comparer) 오버로드 사용 :

public class Compare : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y)
    {
        return x.Name == y.Name;
    }
    public int GetHashCode(Person codeh)
    {
        return codeh.Name.GetHashCode();
    }
}

var result5 = objects.Contains(new Person { Name = "Phil" }, new Compare()); // true

Contains 의 현명한 사용법은 여러 if 절을 Contains 호출로 대체하는 것입니다.

그래서 이렇게하는 대신 :

if(status == 1 || status == 3 || status == 4)
{
    //Do some business operation
}
else
{
    //Do something else
}

이 작업을 수행:

if(new int[] {1, 3, 4 }.Contains(status)
{
    //Do some business operaion
}
else 
{
    //Do something else
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow