CodeGym /행동 /C# SELF /람다 표현식 ( =>)을 파라미터로...

람다 표현식 ( =>)을 파라미터로 전달하기

C# SELF
레벨 50 , 레슨 2
사용 가능

1. 소개

지금까지 본 것처럼 우리는 LINQ 메서드에 람다를 넘겨 사용했어. 그런데 C#의 메서드는 어떻게 함수(람다)를 받을 수 있다는 걸 알까?

간단히 말하면, 델리게이트는 함수용 인터페이스 같은 거야: 시그니처(매개변수 타입과 반환 타입)를 정의하면, 그 시그니처와 맞는 모든 메서드(람다 포함)를 델리게이트를 기대하는 곳에 넘길 수 있어.

문자열을 파라미터로 넘겼던 걸 기억해? 로직을 넘기는 것도 비슷한데, 파라미터 타입이 델리게이트일 뿐이야.

델리게이트: 쉽게 풀어쓴 기본 이론

C#에서 델리게이트는 "이런 시그니처를 가진 함수"를 설명하는 타입이야.

// int를 받고 bool을 반환하는 델리게이트
public delegate bool IntPredicate(int x);

시그니처가 맞는 어떤 함수든 이 타입의 변수에 할당할 수 있어:

bool IsEven(int n) => n % 2 == 0;

IntPredicate pred = IsEven;

그리고 이렇게 람다도 할당돼:

IntPredicate pred = x => x % 2 == 0;

제네릭 델리게이트: Func, Action, Predicate

  • Func<T1, ..., TResult> — T1, ... 같은 파라미터를 받고 TResult를 반환하는 함수.
  • Action<T1, ...> — 파라미터는 받지만 값을 반환하지 않는 함수(void).
  • Predicate<T>T를 받고 bool을 반환하는 함수.

2. 람다를 내 메서드에 전달하기

예를 들어 우리가 학습용 미니 프로젝트 — 사용자 리스트를 다루는 콘솔 앱 — 를 발전시키고 있다고 해보자. 이전엔 컬렉션을 LINQ로 필터링했지. 이제는 람다 조건을 받는 자체 메서드를 만들어 보자.

람다 파라미터가 있는 메서드 만들기

// 예시용 User 클래스 정의 (애플리케이션에 추가)
public class User
{
    public string Name { get; set; }
    public bool IsActive { get; set; }
}

// 리스트와 델리게이트 조건(람다)을 받는 메서드
public static List<User> FilterUsers(List<User> users, Predicate<User> predicate)
{
    var result = new List<User>();
    foreach (var user in users)
    {
        if (predicate(user)) // 람다를 호출!
            result.Add(user);
    }
    return result;
}

이제 어떤 람다든 전달할 수 있어:

var users = new List<User>
{
    new User { Name = "바샤", IsActive = true },
    new User { Name = "페챠", IsActive = false },
    new User { Name = "마샤", IsActive = true }
};

// 활성 사용자만 필터링
var activeUsers = FilterUsers(users, user => user.IsActive);

foreach (var user in activeUsers)
    Console.WriteLine(user.Name); // 바샤, 마샤

끝이야! 메서드 FilterUsersPredicate<User>를 기대하니까, 거기에 맞는 람다를 그냥 전달한 거야 — 작은 로직 조각을 파라미터로 넘긴 셈이지.

Func<T, TResult>를 쓰는 경우

Predicate<T>는 조건(즉 bool 반환)이 필요할 때 좋아. 그런데 각 사용자에 대해 뭔가를 "계산"해서 모으고 싶다면?

// 각 요소에 함수를 적용해서 결과를 모으는 메서드
public static List<TResult> MapUsers<TResult>(List<User> users, Func<User, TResult> selector)
{
    var result = new List<TResult>();
    foreach (var user in users)
    {
        result.Add(selector(user));
    }
    return result;
}

사용 예:

var names = MapUsers(users, user => user.Name.ToUpper());

foreach (var name in names)
    Console.WriteLine(name); // ВАСЯ, ПЕТЯ, МАША

3. 유용한 팁

다양한 전달 형태

람다뿐 아니라 일반 메서드도 전달할 수 있어 — 시그니처만 맞으면 돼.

// 일반 메서드
static bool NameHasS(User user) => user.Name.Contains("с");

// 일반 메서드 전달:
var usersWithS = FilterUsers(users, NameHasS);
// 람다 전달
var usersWithA = FilterUsers(users, u => u.Name.Contains("а"));

옛 스타일의 익명 메서드도 가능한데(권장하진 않음):

var usersWithM = FilterUsers(users, delegate(User u) { return u.Name.Contains("м"); });

요즘 스타일은 당연히 람다야!

LINQ에 람다 전달하기: 실제로 무슨 일이 일어나는가

var result = users.Where(u => u.IsActive).ToList();

내부적으로 WhereFunc<User, bool>를 받는다는 걸 알아두자. 그래서 Func<...>를 받는 어떤 메서드도 비슷하게 사용할 수 있어.

두 개의 파라미터가 필요하면?

// 필터용으로 두 개의 람다를 받는 메서드
public static List<User> FilterUsersCustom(
    List<User> users,
    Func<User, bool> include,
    Func<User, bool> exclude)
{
    var result = new List<User>();
    foreach (var user in users)
    {
        if (include(user) && !exclude(user))
            result.Add(user);
    }
    return result;
}

사용 예:

var customFiltered = FilterUsersCustom(
    users,
    u => u.Name.StartsWith("В"),
    u => u.IsActive == false
);
// 이름이 "В"로 시작하고 활성 상태인 사용자만 가져올 거야

시나리오: 필터 팩토리

Console.WriteLine("이름의 최소 길이를 입력하세요:");
int minLength = int.Parse(Console.ReadLine());

Predicate<User> lengthFilter = user => user.Name.Length >= minLength;
var filteredUsers = FilterUsers(users, lengthFilter);

// 꽤 인터랙티브하고 생동감 있어!

4. 흔한 오류와 주의사항

컴파일러가 람다의 파라미터 타입을 "추론"하지 못하는 경우가 있어 — 특히 오버로드가 많거나 메서드가 특정 반환 타입/여러 파라미터를 요구할 때. 그럴 땐 람다의 타입을 명시해주면 돼:

FilterUsers(users, (User u) => u.Name.Length > 3);

또는 이렇게도 가능:

MapUsers(users, (User u) => u.Name.ToUpper());

오류: 람다가 시그니처에 맞지 않음

FilterUsers(users, user => Console.WriteLine(user.Name)); // 오류! bool이 예상되었는데 void가 나옴

이는 기대되는 함수가 bool을 반환하는데, 이 람다는 void를 반환(명시적으로 아무것도 반환하지 않음)하기 때문이야. 반환 타입을 항상 확인하자!

오류: 람다 남용

람다가 10줄 이상으로 길어지면 별도 메서드로 빼는 게 좋아. 가독성도 좋아지고 디버깅도 쉬워져.

2
과제
C# SELF, 레벨 50, 레슨 2
잠금
복잡한 사용자 필터링 및 변환
복잡한 사용자 필터링 및 변환
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION