C# LINQ를 활용한 데이터 쿼리 기법

1. 데이터 필터링

LINQ(Language Integrated Query)는 컬렉션의 데이터를 특정 조건에 따라 걸러내는 강력한 방법을 제공합니다. 주로 where 절을 사용하여 조건을 정의하며, 이는 쿼리 구문 또는 메서드 구문으로 작성될 수 있습니다.

쿼리 구문으로 필터링


public static void FilterDataQuerySyntax()
{
    var eliteDrivers = from d in Formula1.GetChampions()
                       where d.Wins > 15 && (d.Country == "Brazil" || d.Country == "Austria")
                       select d;

    foreach (var driver in eliteDrivers)
    {
        Console.WriteLine($"{driver:A}");
    }
}

메서드 구문으로 필터링


public static void FilterDataMethodSyntax()
{
    var eliteDrivers = Formula1.GetChampions()
        .Where(d => d.Wins > 15 && (d.Country == "Brazil" || d.Country == "Austria"));

    foreach (var driver in eliteDrivers)
    {
        Console.WriteLine($"{driver:A}");
    }
}

2. 인덱스를 활용한 필터링

Where 메서드의 오버로드 중 하나는 현재 요소의 인덱스를 함께 전달받는 기능을 제공합니다. 이를 통해 요소의 값뿐만 아니라 컬렉션 내에서의 위치를 기반으로 필터링할 수 있습니다.


public static void FilterByIndex()
{
    // 성이 'A'로 시작하고 인덱스가 홀수인 드라이버를 반환합니다.
    var indexedDrivers = Formula1.GetChampions()
        .Where((d, idx) => d.LastName.StartsWith("A") && idx % 2 != 0); // 인덱스 0부터 시작, 홀수 번째

    foreach (var driver in indexedDrivers)
    {
        Console.WriteLine($"{driver:A}");
    }
}

3. 타입 기반 필터링

OfType<T>() 확장 메서드는 혼합된 타입의 컬렉션에서 특정 타입의 요소만 추출할 때 유용합니다. 비제네릭 컬렉션이나 object 배열에서 특정 타입만 필터링할 때 자주 사용됩니다.


public static void FilterByType()
{
    object[] mixedItems = { "apple", 123, 4.5, "banana", true, "cherry" };
    // OfType<string>()을 사용하여 컬렉션에서 문자열 타입의 요소만 추출합니다.
    var stringElements = mixedItems.OfType<string>();

    foreach (var item in stringElements)
    {
        Console.WriteLine(item);
    }
}

4. 복합 From 절 (SelectMany)

여러 개의 from 절을 사용하거나 SelectMany 메서드를 사용하면 중첩된 컬렉션의 요소를 단일 평면화된 시퀀스로 결합하여 쿼리할 수 있습니다. 이는 한 객체의 하위 컬렉션에 접근하여 필터링할 때 유용합니다.

쿼리 구문으로 복합 From


public static void CompoundFromQuerySyntax()
{
    // 첫 번째 from 절은 Racer 객체를 가져오고, 두 번째 from 절은 각 Racer의 Cars 속성을 탐색합니다.
    // Ferrari를 운전한 챔피언들을 성 기준으로 정렬하여 이름을 반환합니다.
    var ferrariPilots = from driver in Formula1.GetChampions()
                                 from carType in driver.Cars
                                 where carType == "Ferrari"
                                 orderby driver.LastName
                                 select $"{driver.FirstName} {driver.LastName}";

    foreach (var pilotName in ferrariPilots)
    {
        Console.WriteLine(pilotName);
    }
}

메서드 구문으로 SelectMany


public static void SelectManyMethodSyntax()
{
    // SelectMany는 각 드라이버와 그들이 운전한 차량을 짝지어 평면화된 시퀀스를 생성합니다.
    // 이후 필터링, 정렬, 프로젝션을 수행합니다.
    var ferrariPilots = Formula1.GetChampions()
        .SelectMany(driver => driver.Cars, (driver, carType) => new { Driver = driver, CarType = carType })
        .Where(item => item.CarType == "Ferrari")
        .OrderBy(item => item.Driver.LastName)
        .Select(item => $"{item.Driver.FirstName} {item.Driver.LastName}");

    foreach (var pilotName in ferrariPilots)
    {
        Console.WriteLine(pilotName);
    }
}

5. 데이터 정렬

orderby 절 또는 OrderBy, OrderByDescending, ThenBy, ThenByDescending 메서드를 사용하여 쿼리 결과를 정렬할 수 있습니다. 여러 기준에 따라 정렬할 수도 있습니다.

내림차순 정렬 (쿼리 구문)


public static void SortDescendingQuerySyntax()
{
    Console.WriteLine("브라질 출신 챔피언들을 우승 횟수 내림차순으로 정렬:");
    Console.WriteLine();

    var brazilianChamps = from driver in Formula1.GetChampions()
                         where driver.Country == "Brazil"
                         orderby driver.Wins descending
                         select driver;

    foreach (var driver in brazilianChamps)
    {
        Console.WriteLine($"{driver:A}");
    }
}

내림차순 정렬 (메서드 구문)


public static void SortDescendingMethodSyntax()
{
    Console.WriteLine("브라질 출신 챔피언들을 우승 횟수 내림차순으로 정렬:");
    Console.WriteLine();
    var brazilianChamps = Formula1.GetChampions()
        .Where(driver => driver.Country == "Brazil")
        .OrderByDescending(driver => driver.Wins);

    foreach (var driver in brazilianChamps)
    {
        Console.WriteLine($"{driver:A}");
    }
}

다중 정렬 (메서드 구문)


public static void SortByMultipleCriteria()
{
    Console.WriteLine("국가, 성, 이름 순으로 정렬된 상위 7명의 챔피언:");
    Console.WriteLine();

    // 먼저 국가별, 다음으로 성별, 마지막으로 이름별로 정렬하고 상위 7개만 가져옵니다.
    var topDriversSorted = Formula1.GetChampions()
                            .OrderBy(driver => driver.Country)
                            .ThenBy(driver => driver.LastName)
                            .ThenBy(driver => driver.FirstName)
                            .Take(7);

    foreach (var driver in topDriversSorted)
    {
        Console.WriteLine($"{driver.Country}: {driver.LastName}, {driver.FirstName}");
    }
}

6. 데이터 그룹화

group by 절 또는 GroupBy 메서드를 사용하여 공통된 키를 가진 요소를 그룹으로 묶을 수 있습니다. 그룹화된 결과에 추가적인 필터링이나 정렬을 적용할 수도 있습니다.

쿼리 구문으로 그룹화


public static void GroupDataQuerySyntax()
{
    // Country 속성을 기준으로 드라이버를 그룹화하고, 그룹 내 드라이버 수가 2명 이상인 국가만 선택합니다.
    // 이후 그룹의 크기 내림차순, 국가 이름 오름차순으로 정렬합니다.
    var countryStats =
        from driver in Formula1.GetChampions()
        group driver by driver.Country into countryGroup
        let driverCount = countryGroup.Count() // let 절을 활용하여 그룹 내 변수 선언
        where driverCount >= 2
        orderby driverCount descending, countryGroup.Key
        select new
        {
            Nation = countryGroup.Key,
            Champions = driverCount
        };

    foreach (var item in countryStats)
    {
        Console.WriteLine($"{item.Nation,-10} {item.Champions}");
    }
}

메서드 구문으로 그룹화


public static void GroupDataMethodSyntax()
{
    var countryStats = Formula1.GetChampions()
      .GroupBy(driver => driver.Country)
      .Select(g => new { Group = g, Count = g.Count() }) // 그룹과 개수를 포함하는 익명 타입 생성
      .OrderByDescending(item => item.Count)
      .ThenBy(item => item.Group.Key)
      .Where(item => item.Count >= 2)
      .Select(item => new
      {
          Nation = item.Group.Key,
          Champions = item.Count
      });

    foreach (var item in countryStats)
    {
        Console.WriteLine($"{item.Nation,-10} {item.Champions}");
    }
}

7. 중첩 객체 그룹화

그룹화된 결과 내에서 다시 쿼리를 수행하여 중첩된 객체 컬렉션을 생성할 수 있습니다. 예를 들어, 특정 국가의 드라이버 목록을 그룹화된 결과에 포함하는 경우입니다.

쿼리 구문으로 중첩 그룹화


public static void GroupNestedObjectsQuerySyntax()
{
    var nationsWithDrivers = from driver in Formula1.GetChampions()
                            group driver by driver.Country into countryGroup
                            let count = countryGroup.Count()
                            where count >= 2
                            orderby count descending, countryGroup.Key
                            select new
                            {
                                Nation = countryGroup.Key,
                                TotalDrivers = count,
                                DriversList = from d in countryGroup
                                              orderby d.LastName
                                              select d.FirstName + " " + d.LastName
                            };

    foreach (var item in nationsWithDrivers)
    {
        Console.WriteLine($"{item.Nation,-10} {item.TotalDrivers}");
        foreach (var name in item.DriversList)
        {
            Console.Write($"{name}; ");
        }
        Console.WriteLine();
    }
}

8. 내부 조인 (Inner Join)

join 절 또는 Join 메서드를 사용하여 두 개 이상의 컬렉션을 공통된 키를 기반으로 결합할 수 있습니다. 내부 조인은 양쪽 컬렉션 모두에서 일치하는 요소만 반환합니다.

쿼리 구문으로 내부 조인


public static void InnerJoinQuerySyntax()
{
    // 각 드라이버 챔피언십 연도를 평면화합니다.
    var driverChampionships = from d in Formula1.GetChampions()
                              from year in d.Years
                              select new
                              {
                                  Year = year,
                                  DriverName = d.FirstName + " " + d.LastName
                              };
    // 각 컨스트럭터 챔피언십 연도를 평면화합니다.
    var constructorChampionships = from team in Formula1.GetConstructorChampions()
                                   from year in team.Years
                                   select new
                                   {
                                       Year = year,
                                       TeamName = team.Name
                                   };
    // 드라이버 챔피언십과 컨스트럭터 챔피언십을 연도 기준으로 조인합니다.
    var combinedTitles = (from driverChamp in driverChampionships
                         join constructorChamp in constructorChampionships on driverChamp.Year equals constructorChamp.Year
                         orderby constructorChamp.Year
                         select new
                         {
                             driverChamp.Year,
                             WorldChampion = driverChamp.DriverName,
                             Constructor = constructorChamp.TeamName
                         }).Take(8); // 상위 8개 결과만 표시

    Console.WriteLine("연도  월드 챔피언\t\t   컨스트럭터 우승팀");
    foreach (var entry in combinedTitles)
    {
        Console.WriteLine($"{entry.Year}: {entry.WorldChampion,-20} {entry.Constructor}");
    }
}

메서드 구문으로 내부 조인


public static void InnerJoinMethodSyntax()
{
    var driverChampionships = Formula1.GetChampions()
        .SelectMany(d => d.Years, (d, year) =>
        new
        {
            Year = year,
            DriverName = $"{d.FirstName} {d.LastName}"
        });

    var constructorChampionships = Formula1.GetConstructorChampions()
        .SelectMany(team => team.Years, (team, year) =>
        new
        {
            Year = year,
            TeamName = team.Name
        });

    var combinedTitles = driverChampionships.Join(
        constructorChampionships,
        driverChamp => driverChamp.Year,
        constructorChamp => constructorChamp.Year,
        (driverChamp, constructorChamp) =>
            new
            {
                driverChamp.Year,
                WorldChampion = driverChamp.DriverName,
                Constructor = constructorChamp.TeamName
            }).OrderBy(entry => entry.Year).Take(8); // 상위 8개 결과만 표시

    Console.WriteLine("연도  월드 챔피언\t\t   컨스트럭터 우승팀");
    foreach (var entry in combinedTitles)
    {
        Console.WriteLine($"{entry.Year}: {entry.WorldChampion,-20} {entry.Constructor}");
    }
}

9. 좌측 외부 조인 (Left Outer Join)

좌측 외부 조인은 첫 번째 컬렉션의 모든 요소를 포함하고, 두 번째 컬렉션에서 일치하는 요소가 없더라도 첫 번째 컬렉션의 요소는 유지합니다. 일치하는 항목이 없을 경우 두 번째 컬렉션의 요소는 기본값(null)으로 처리됩니다. GroupJoinDefaultIfEmpty를 조합하여 구현할 수 있습니다.

쿼리 구문으로 좌측 외부 조인


public static void LeftOuterJoinQuerySyntax()
{
    var driverWins = from d in Formula1.GetChampions()
                     from year in d.Years
                     select new
                     {
                         Year = year,
                         Driver = d.FirstName + " " + d.LastName
                     };

    var constructorWins = from team in Formula1.GetConstructorChampions()
                          from year in team.Years
                          select new
                          {
                              Year = year,
                              Team = team.Name
                          };

    // 드라이버 우승 연도에 해당하는 컨스트럭터 우승팀이 없는 경우에도 드라이버 정보는 유지합니다.
    var championshipSummary =
      (from dw in driverWins
       join cw in constructorWins on dw.Year equals cw.Year into teamResults
       from cw in teamResults.DefaultIfEmpty()
       orderby dw.Year
       select new
       {
           dw.Year,
           ChampionDriver = dw.Driver,
           WinningConstructor = cw == null ? "미정 또는 해당 없음" : cw.Team
       }).Take(12); // 상위 12개 결과만 표시

    Console.WriteLine("연도  챔피언 드라이버\t   우승 컨스트럭터");
    foreach (var item in championshipSummary)
    {
        Console.WriteLine($"{item.Year}: {item.ChampionDriver,-20} {item.WinningConstructor}");
    }
}

메서드 구문으로 좌측 외부 조인


public static void LeftOuterJoinMethodSyntax()
{
    var driverWins = Formula1.GetChampions()
        .SelectMany(d => d.Years, (d, year) =>
        new
        {
            Year = year,
            Driver = $"{d.FirstName} {d.LastName}"
        });

    var constructorWins = Formula1.GetConstructorChampions()
        .SelectMany(team => team.Years, (team, year) =>
        new
        {
            Year = year,
            Team = team.Name
        });

    var championshipSummary =
        driverWins.GroupJoin(
            constructorWins,
            dw => dw.Year,
            cw => cw.Year,
            (dw, teamResults) => new
            {
                Year = dw.Year,
                ChampionDriver = dw.Driver,
                WinningConstructors = teamResults
            })
            .SelectMany(
                item => item.WinningConstructors.DefaultIfEmpty(),
                (dw, cw) => new
                {
                    Year = dw.Year,
                    ChampionDriver = dw.ChampionDriver,
                    WinningConstructor = cw?.Team ?? "미정 또는 해당 없음"
                })
            .OrderBy(item => item.Year)
            .Take(12); // 상위 12개 결과만 표시

    Console.WriteLine("연도  챔피언 드라이버\t   우승 컨스트럭터");
    foreach (var item in championshipSummary)
    {
        Console.WriteLine($"{item.Year}: {item.ChampionDriver,-20} {item.WinningConstructor}");
    }
}

10. 그룹 조인 (Group Join)

그룹 조인은 한 컬렉션의 각 요소와 다른 컬렉션에서 일치하는 요소들을 하위 그룹으로 연결합니다. 이는 한 부모에 여러 자식 레코드를 연결할 때 유용합니다. `GroupJoin` 메서드는 쿼리 구문의 `join ... into` 절과 유사합니다.


public static void GroupJoinExample()
{
    // 각 챔피언십 연도의 상위 3위 드라이버 정보를 평면화합니다.
    var podiumFinishers = Formula1.GetChampionships()
        .SelectMany(cs => new List<(int Year, int Position, string FirstName, string LastName)>
        {
            (cs.Year, Position: 1, FirstName: cs.First.FirstName(), LastName: cs.First.LastName()),
            (cs.Year, Position: 2, FirstName: cs.Second.FirstName(), LastName: cs.Second.LastName()),
            (cs.Year, Position: 3, FirstName: cs.Third.FirstName(), LastName: cs.Third.LastName())
        });

    // 모든 챔피언 드라이버에 대해, 해당 드라이버가 연도별 포디움에 올랐던 기록들을 그룹으로 연결합니다.
    var driverPodiumRecords = Formula1.GetChampions()
        .GroupJoin(podiumFinishers,
            champ => (champ.FirstName, champ.LastName), // 복합 키
            podium => (podium.FirstName, podium.LastName), // 복합 키
            (champ, annualResults) =>
                (champ.FirstName, champ.LastName, champ.Wins, champ.Starts, PodiumResults: annualResults));

    foreach (var driverData in driverPodiumRecords)
    {
        Console.WriteLine($"{driverData.FirstName} {driverData.LastName} (우승: {driverData.Wins}, 출전: {driverData.Starts})");
        foreach (var result in driverData.PodiumResults)
        {
            Console.WriteLine($"\t- {result.Year}년 {result.Position}위");
        }
    }
}

11. 집합 연산

LINQ는 두 컬렉션에 대한 합집합, 교집합, 차집합 등의 집합 연산을 수행하는 메서드를 제공합니다. 주요 메서드로는 Distinct(), Union(), Intersect(), Except()가 있습니다.


public static void SetOperationsExample()
{
    IEnumerable<Racer> getDriversByCarBrand(string brand) =>
        from r in Formula1.GetChampions()
        from car in r.Cars
        where car == brand
        orderby r.LastName
        select r;

    Console.WriteLine("Ferrari와 McLaren 둘 다 운전한 월드 챔피언:");
    // Ferrari를 운전한 드라이버와 McLaren을 운전한 드라이버의 교집합을 찾습니다.
    foreach (var driver in getDriversByCarBrand("Ferrari").Intersect(getDriversByCarBrand("McLaren")))
    {
        Console.WriteLine(driver);
    }

    Console.WriteLine("\n챔피언은 아니지만 포디움에 올랐던 드라이버:");
    // 모든 챔피언십 연도의 상위 3위 드라이버 정보 (RacerInfo 구조체가 필요하다고 가정)
    // RacerInfo는 FirstName, LastName 속성을 가진다고 가정합니다.
    var allPodiumFinishers = Formula1.GetChampionships().SelectMany(cs => new List<RacerInfo>()
    {
        new RacerInfo { Year = cs.Year, Position = 1, FirstName = cs.First.FirstName(), LastName = cs.First.LastName() },
        new RacerInfo { Year = cs.Year, Position = 2, FirstName = cs.Second.FirstName(), LastName = cs.Second.LastName() },
        new RacerInfo { Year = cs.Year, Position = 3, FirstName = cs.Third.FirstName(), LastName = cs.Third.LastName() }
    });

    var nonChampionsOnPodium = allPodiumFinishers.Select(p => new { p.FirstName, p.LastName })
                                                .Distinct() // 중복 제거
                                                .Except(Formula1.GetChampions().Select(c => new { c.FirstName, c.LastName }));

    foreach (var driver in nonChampionsOnPodium)
    {
        Console.WriteLine($"{driver.FirstName} {driver.LastName}");
    }
}

// 예시를 위한 RacerInfo 구조체 (실제 코드에서는 Formula1.cs에 정의되어 있다고 가정)
public struct RacerInfo
{
    public int Year { get; set; }
    public int Position { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

12. Zip 연산

Zip() 메서드는 두 개의 시퀀스를 병렬로 결합하여 새로운 시퀀스를 생성합니다. 각 시퀀스의 첫 번째 요소들을 짝지어 결합하고, 다음으로 두 번째 요소들을 짝지어 결합하는 식으로 진행됩니다. 두 시퀀스의 길이가 다를 경우, 더 짧은 시퀀스의 끝에 도달하면 결합을 멈춥니다.


public static void ZipOperationExample()
{
    var italianDriverNames = from r in Formula1.GetChampions()
                             where r.Country == "Italy"
                             orderby r.Wins descending
                             select r.FirstName + " " + r.LastName;

    var italianDriverStarts = from r in Formula1.GetChampions()
                                     where r.Country == "Italy"
                                     orderby r.Wins descending
                                     select r.Starts;

    // 이탈리아 드라이버의 이름과 출전 횟수를 병합합니다.
    var combinedInfo = italianDriverNames.Zip(italianDriverStarts,
                                          (name, starts) => $"{name}, 출전: {starts}회");

    foreach (var info in combinedInfo)
    {
        Console.WriteLine(info);
    }
}

13. 시퀀스 분할 (Partitioning)

Skip()Take() 메서드를 사용하여 시퀀스의 일부를 건너뛰거나 특정 개수만큼 가져와 시퀀스를 분할할 수 있습니다. 이는 페이징 기능을 구현할 때 주로 사용됩니다.


public static void PartitioningExample()
{
    int entriesPerPage = 7; // 페이지당 항목 수

    int totalChampions = Formula1.GetChampions().Count();
    int numberOfPages = (int)Math.Ceiling(totalChampions / (double)entriesPerPage);

    for (int page = 0; page < numberOfPages; page++)
    {
        Console.WriteLine($"--- 페이지 {page + 1} ---");
        // 현재 페이지에 해당하는 항목만 건너뛰고 가져옵니다.
        var paginatedDrivers =
           (from driver in Formula1.GetChampions()
            orderby driver.LastName, driver.FirstName
            select driver.FirstName + " " + driver.LastName)
           .Skip(page * entriesPerPage)
           .Take(entriesPerPage);

        foreach (var name in paginatedDrivers)
        {
            Console.WriteLine(name);
        }
        Console.WriteLine();
    }
}

14. 집계 연산자

LINQ는 시퀀스에 대한 통계 정보를 계산하는 다양한 집계 연산자를 제공합니다. Count(), Sum(), Min(), Max(), Average() 등이 대표적입니다.

Count() 예제


public static void AggregateCountExample()
{
    // 3회 이상 챔피언십을 우승한 드라이버를 우승 횟수 내림차순, 성 오름차순으로 정렬합니다.
    var multiTimeChampions = from r in Formula1.GetChampions()
                        let champYears = r.Years.Count()
                        where champYears >= 3
                        orderby champYears descending, r.LastName
                        select new
                        {
                            FullName = r.FirstName + " " + r.LastName,
                            ChampionshipsWon = champYears
                        };

    foreach (var record in multiTimeChampions)
    {
        Console.WriteLine($"{record.FullName} (총 우승: {record.ChampionshipsWon}회)");
    }
}

Sum() 예제


public static void AggregateSumExample()
{
    // 각 국가별 총 우승 횟수를 계산하고, 우승 횟수 내림차순, 국가 이름 오름차순으로 정렬합니다.
    var countryWinsSummary = (from countryGroup in
                                 from r in Formula1.GetChampions()
                                 group r by r.Country into countryGrouping
                                 select new
                                 {
                                     Nation = countryGrouping.Key,
                                     TotalWins = (from driver in countryGrouping
                                                  select driver.Wins).Sum()
                                 }
                             orderby countryGroup.TotalWins descending, countryGroup.Nation
                             select countryGroup).Take(4); // 상위 4개 국가만 표시

    foreach (var summary in countryWinsSummary)
    {
        Console.WriteLine($"{summary.Nation,-10} 총 우승: {summary.TotalWins}회");
    }
}

15. 변환 연산자

변환 연산자는 쿼리 결과를 다른 형태의 컬렉션으로 변환하거나, 쿼리를 즉시 실행하여 결과를 얻습니다. ToList(), ToArray(), ToDictionary(), ToLookup(), Cast<T>() 등이 있습니다.

ToList() 예제


public static void ConvertToListExample()
{
    // ToList()를 호출하여 쿼리를 즉시 실행하고 결과를 List<Racer>에 저장합니다.
    List<Racer> highStartDrivers = (from r in Formula1.GetChampions()
                                  where r.Starts > 200
                                  orderby r.Starts descending
                                  select r).ToList();

    foreach (var driver in highStartDrivers)
    {
        Console.WriteLine($"{driver} (총 출전: {driver:S})");
    }
}

ToLookup() 예제

Lookup<TKey, TElement>Dictionary<TKey, TValue>와 유사하지만, 하나의 키에 여러 값을 연결할 수 있는 다대일(one-to-many) 매핑을 지원합니다.


public static void ConvertToLookupExample()
{
    // 드라이버가 운전했던 차량을 키로, 해당 드라이버 객체를 값으로 하는 Lookup을 생성합니다.
    var carToDriverLookup = (from r in Formula1.GetChampions()
                          from car in r.Cars
                          select new
                          {
                              CarBrand = car,
                              Driver = r
                          }).ToLookup(item => item.CarBrand, item => item.Driver);

    if (carToDriverLookup.Contains("Williams"))
    {
        Console.WriteLine("\nWilliams 팀에서 뛰었던 드라이버:");
        foreach (var williamsDriver in carToDriverLookup["Williams"])
        {
            Console.WriteLine(williamsDriver);
        }
    }
}

Cast<T>() 예제

Cast<T>()는 비제네릭 컬렉션(예: ArrayList)의 요소를 특정 타입으로 변환하여 LINQ 쿼리를 적용할 수 있도록 합니다. 이는 컴파일 타임에 타입 안전성을 제공합니다.


public static void ConvertWithCastExample()
{
    var untypedDrivers = new System.Collections.ArrayList(Formula1.GetChampions() as System.Collections.ICollection);
    // ArrayList와 같은 비제네릭 컬렉션에 LINQ 쿼리를 사용하기 위해 Cast<Racer>()를 적용합니다.
    var usDrivers = from r in untypedDrivers.Cast<Racer>()
                        where r.Country == "USA"
                        orderby r.Wins descending
                        select r;
    
    Console.WriteLine("\n미국 출신 챔피언:");
    foreach (var driver in usDrivers)
    {
        Console.WriteLine($"{driver:A}");
    }
}

16. 생성 연산자

생성 연산자는 새로운 시퀀스를 생성하는 정적 메서드입니다. Enumerable.Range(), Enumerable.Empty<T>(), Enumerable.Repeat<T>() 등이 있으며, 이는 확장 메서드가 아닌 Enumerable 클래스의 정적 메서드입니다.


public static void GenerateSequenceRange()
{
    // 10부터 시작하여 15개의 정수를 포함하는 시퀀스를 생성합니다.
    var sequentialNumbers = Enumerable.Range(10, 15);
    Console.WriteLine("생성된 시퀀스 (10부터 15개):");
    foreach (var num in sequentialNumbers)
    {
        Console.Write($"{num} ");
    }
    Console.WriteLine();

    // Enumerable.Empty<T>()는 빈 시퀀스를 반환하며, 
    // Enumerable.Repeat<T>(value, count)는 특정 값을 지정된 횟수만큼 반복하는 시퀀스를 생성합니다.
}

태그: C# LINQ QuerySyntax MethodSyntax Filtering

7월 3일 19:13에 게시됨