CodeGym /행동 /C# SELF /집계 함수: Sum,

집계 함수: Sum, Count, Average, Max, Min

C# SELF
레벨 32 , 레슨 1
사용 가능

1. 소개

컬렉션을 다룰 때 그냥 하나씩 돌면서 뭔가 하는 게 아니라, 전체 데이터에 대한 간단한 "요약"이 필요할 때가 많아. 예를 들면:

  • 5점을 받은 학생이 몇 명인지 세기.
  • 학교에 학생이 총 몇 명인지 알아보기.
  • 시험에서 학생들이 받은 점수의 합계 구하기.
  • 최고점, 최저점 찾기.
  • 반 평균 점수 계산하기.

물론 이런 건 그냥 카운터 변수 두고 for문 돌려도 되지. 근데 솔직히 이런 단순한 작업에 코드 20줄 쓰는 거 귀찮잖아.

LINQ에는 집계 함수라는 게 있어 — 이미 만들어진 메서드들이고, 컬렉션을 받아서 모든 요소를 쭉 돌고, 결과(합계, 평균, 최대, 최소 등)를 바로 내놔. 가끔은 더 복잡한 것도 가능하고.

여기 "집계 함수 표" 간단 정리:

메서드 무엇을 하나 반환값
Count()
요소 개수 세기
int
Sum()
숫자 값의 합계 구하기 요소 타입에 따라 다름 (
int
,
double
, ...)
Average()
평균값 계산
double
또는 다른 숫자 타입
Max()
최대값 찾기 컬렉션의 요소
Min()
최소값 찾기 컬렉션의 요소

이 메서드들은 전부 IEnumerable<T>를 구현한 컬렉션의 extension 메서드야. 자세한 건 LINQ 집계 메서드 공식 문서 참고!

2. 실습용 데이터 준비

계속 학생 관리용 간단한 앱으로 예제 해볼게. 이런 클래스가 있다고 치자:


// 학생 모델
public class Student
{
    public string Name { get; set; }
    public int Grade { get; set; } // 5점 만점 점수
    public string Email { get; set; }
}

그리고 리스트:


// 기본 학생 리스트
List<Student> students = new List<Student>
{
    new Student { Name = "이반", Grade = 5, Email = "ivan@example.com" },
    new Student { Name = "올가", Grade = 4, Email = "olga@example.com" },
    new Student { Name = "아르템", Grade = 3, Email = "artem@example.com" },
    new Student { Name = "다리야", Grade = 5, Email = "darya@example.com" },
    new Student { Name = "표트르", Grade = 2, Email = "petr@example.com" }
};

3. 요소 개수 세기: Count()

가장 기본적인 통계

학생이 총 몇 명인지 알고 싶으면:


int totalStudents = students.Count(); // 5
Console.WriteLine($"총 학생 수: {totalStudents}");

LINQ 매직: 진짜 간단! Count()는 컬렉션의 요소 개수를 반환해.

조건부 개수 세기

그럼 5점 받은 학생은 몇 명?


int excellentStudents = students.Count(s => s.Grade == 5);
Console.WriteLine($"5점 받은 학생: {excellentStudents}");

여기선 Count에 람다를 넘겨서 조건에 맞는 학생만 세는 거야 (s.Grade == 5). LINQ 내부적으로 Where(...).Count()랑 똑같은데, 더 짧고 약간 더 효율적이야.

컬렉션이 비어 있으면?

컬렉션이 비어 있으면 Count0을 반환해 — 에러 안 나.

4. 값 합계 구하기: Sum()

모든 점수 합계 구하기

반 전체 점수 합계를 알고 싶으면:


int sumOfGrades = students.Sum(s => s.Grade); // 5+4+3+5+2 = 19
Console.WriteLine($"점수 합계: {sumOfGrades}");

Sum은 셀렉터(람다식)를 받아서 각 요소의 값을 뽑아와.

숫자 배열 합계

그냥 숫자 리스트면 셀렉터 필요 없음:


int[] numbers = { 1, 2, 3, 4, 5 };
int sum = numbers.Sum(); // 15

흔한 실수와 주의점

컬렉션이 비어 있으면 Sum()은 숫자 타입이면 0을 반환해. 근데 nullable 타입(int?, double?)이면 Sumnull값은 무시하고 계산해.

5. 평균값: Average()

반 평균 점수 구하기

클래식: 학생 평균 점수는?


double averageGrade = students.Average(s => s.Grade); // (5+4+3+5+2)/5 = 3.8
Console.WriteLine($"평균 점수: {averageGrade:F2}");

Average는 통계 낼 때 진짜 유용해. 반환값은 항상 double이야, 원래 값이 int여도. 소수점 잘려나가는 거 방지하려고.

요소가 하나도 없으면

컬렉션이 비어 있으면 Average()InvalidOperationException 예외를 던져. 이거 진짜 자주 당하는 함정이야: 컬렉션에 뭐가 있는지 확실하지 않으면 미리 체크해!


if (students.Any())
    Console.WriteLine(students.Average(s => s.Grade));
else
    Console.WriteLine("평균 계산할 데이터 없음!");

숫자 배열 평균


double avg = numbers.Average();

6. 최대값과 최소값: Max()Min()

누가 반 에이스고, 누가 바닥인지

누가 반에서 점수 제일 높고, 누가... 음, 선생님 걱정 끼치는지 알고 싶으면:


int maxGrade = students.Max(s => s.Grade); // 5
int minGrade = students.Min(s => s.Grade); // 2

Console.WriteLine($"최고 점수: {maxGrade}");
Console.WriteLine($"최저 점수: {minGrade}");

그럼 그 주인공은 누구?

가끔은 점수만 말고, 그 점수 받은 학생 이름이 더 중요하지. 최고 점수 학생 객체 뽑기:


// 점수 내림차순 정렬 후 첫 번째 학생
var bestStudent = students.OrderByDescending(s => s.Grade).First();
Console.WriteLine($"최고 학생: {bestStudent.Name} ({bestStudent.Grade})");

최신 .NET 버전엔 MaxBy도 있어서 더 깔끔하게 할 수 있어. .NET 6부터 쓸 수 있고, .NET 9에선 더 편해졌어 (참고: MaxBy Microsoft Docs). 근데 MaxBy 없어도 정렬 + .First()로 충분히 가능.

함정과 차이점

컬렉션이 비어 있으면 Max()Min()InvalidOperationException 예외를 던져. (컬렉션 미리 안 체크하면 특히 조심!)


if (students.Any())
    Console.WriteLine($"최고 점수: {students.Max(s => s.Grade)}");
else
    Console.WriteLine("최대값 찾을 학생 없음.");

7. 집계 메서드 "체이닝" 예시

집계 함수는 필터링, 변환이랑 같이 자주 써:


// 5점 받은 학생 평균 (항상 5지만 예시로!)
double avgExcellent = students
    .Where(s => s.Grade == 5)
    .Average(s => s.Grade);

// 4점 이상 학생 점수 합계
int sumGood = students
    .Where(s => s.Grade >= 4)
    .Sum(s => s.Grade);

// 유니크 이메일 개수 (혹시 몰라서)
int uniqueEmails = students
    .Select(s => s.Email)
    .Distinct()
    .Count();

여기서 LINQ의 진짜 힘이 나와: 연산을 조합해서 코드가 엄청 간결하고 읽기 쉬워져. 네 고양이도 이해할 듯 (고양이가 Junior C# Developer라면).

8. "수작업" 방식과 비교: 왜 집계 함수 쓰는가?

LINQ랑 그냥 for문 돌리는 거랑 비교해보자. 예를 들어 평균 점수 구하기:

일반 방식:


int sum = 0;
int count = 0;
foreach (var s in students)
{
    sum += s.Grade;
    count++;
}
double average = (count != 0) ? (double)sum / count : 0;

LINQ:


double average = students.Average(s => s.Grade);

빈 컬렉션 처리도 LINQ가 훨씬 간단!

9. 유용한 팁

성능과 구현 특징 간단 정리

LINQ 집계 함수는 대부분의 LINQ 연산과 달리 즉시 실행돼! Sum(), Count(), Average(), Max(), Min()을 호출하면 그 순간 컬렉션을 다 돈다는 뜻. 결과는 컬렉션이 아니라 하나의 값이야.

이게 중요한 이유: 집계 함수 전에 무거운 필터나 변환을 했다면, 그 연산은 집계 함수 호출할 때 딱 한 번만 실행돼.

집계 함수도 query-syntax에서 쓸 수 있나?

LINQ 처음 배우면 "이거 query-syntax로도 다 쓸 수 있어?"라고 많이 물어봐. 짧게 말하면: query-syntax엔 집계 함수용 키워드가 없지만, method syntax랑 섞어서 쓰면 돼:


var avg = (from s in students where s.Grade > 3 select s.Grade).Average();

괄호 안은 query-syntax, 마지막에 집계 함수 메서드 호출. 실제로 이렇게 많이 써!

10. LINQ 쓸 때 흔한 실수

실수 1: Average(), Max(), Min()을 빈 컬렉션에 쓰기.
컬렉션이 비어 있으면 Sum()이랑 Count()0을 잘 반환하지만, Average(), Max(), Min()은 예외를 던져. 이 함수들 쓸 땐 요소가 최소 하나 있는지 꼭 확인!

실수 2: 잘못된 시그니처의 람다 넘기기.
예를 들어, 집계 함수(Sum, Max 등)에 숫자 대신 문자열을 넘기면 컴파일 에러 나. 익명 메서드 쓸 때 특히 실수하기 쉬워.

실수 3: 요소 개수 체크 비효율적으로 하기.
Where(...).Count()는 먼저 새 컬렉션 만들고, 그 다음 개수 세. 대신 Count(predicate)를 쓰면 바로 조건에 맞는 개수만 세서 더 빨라.

실수 4: nullable 타입 집계할 때 특징 무시하기.
int?로 합계 구하면 Sum()null값은 무시해. 이게 맞는 동작이지만, 미리 생각 안 하면 결과가 예상과 다를 수 있어.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION