C#을 비롯한 여러 언어들에서 람다 식을 지원하는데
이 람다 식이란 과연 무엇일까?
람다식의 기본 원형은 다음 두가지가 있을 수 있다.
(parameter) => expression
(parameter) => { statements };
이렇게만 봐서는 전혀 모르겠으니 아래를 보자
람다 식은 도대체 뭘까?
람다 식을 보다 정확하게 이해하려면 우선 delegate(대리자)에 대해 알아야 한다.
delegate에 대해 설명하는 글은 여기에 있다.
// 링크
글을 읽었다 가정하고, delegate를 이용해 익명 메소드를 전달하는 방식 중 제일 단점은
매번 delegate라는 키워드로 함수를 감싸줘야 한다는것이다.
이 불편함을 해소하고자 Lamda Expression이 나오게 되었다.
람다식은 익명메소드의 완벽한 상위호환 버전이다.
따라서 람다식만 알아도 충분한데, 왜 익명메소드에 대해 설명하는 글이나 정보들이 있을까?
그 이유는 익명메소드가 나온 다음에 람다식이 나오게 되었는데 MS에서 기존 코드와의 호환성을 위해
굳이 익명메소드를 삭제하지 않고 람다식을 추가했기 때문이다.
아무튼 이 불편함을 없애고자 나온게 Lamda Expression이다.
간단한 익명메소드 사용예시를 보면 다음과 같다.
class Test
{
delegate int Chain(int a, int b);
static void Main()
{
Chain chain = delegate(int a, int b)
{
return a+b;
}
Console.WriteLine(chain(3,4)); // 7출력
}
}
파라미터로 int a, int b를 받고 이 둘을 더한값을 리턴하는 함수를 delegate에 등록해주고 delegate 인스턴스를 이용해
마치 함수를 호출하듯이 사용했다.
람다식을 사용하면 어떻게 될까?
class Test
{
delegate int Chain(int a, int b);
static void Main()
{
Chain chain = (int a, int b) =>
{
return a+b;
}
Console.WriteLine(chain(3,4)); // 7출력
}
}
delegate 키워드가 사라지고 => 라는 코드가 추가되었다.
여기서 조금 더 나아가 parameter의 자료형이 생략 가능하다.
class Test
{
delegate int Chain(int a, int b);
static void Main()
{
Chain chain = (a, b) =>
{
return a+b;
}
Console.WriteLine(chain(3,4)); // 7출력
}
}
이게 될 수 있는 이유는 delegate에 등록할 때 등록할 delegate의 parameter들을 보고 자료형을 유추 할 수 있기 때문이다.
끝이 아니다, return a+b 는 비교적 간단하여 이를 brace로 묶지 않고 생략할 수 있다.
또 기본적으로 값을 반환하다고 가정하여 return문을 생략할 수 있다.
class Test
{
delegate int Chain(int a, int b);
static void Main()
{
Chain chain = (a, b) => { return a+b };
Chain chain = (a, b) => a+b;
// 위의 람다 식 두가지 표현은 완전 동일하다
Console.WriteLine(chain(3,4)); // 7출력
}
}
여기까지 봤을 때, 기존 익명메소드의 귀찮은점을 많이 해결한것 같지만
하나 남은게 있는데 그것은 delegate 선언이다.
하지만 이것도 없앨 수 있는데
System 네임스페이스에는 MS가 미리 선언해놓은 delegate인 Action과 Func가 다음과 같이 선언되어 있다.
최대 16개까지의 parameter를 전달할 수 있게 override 되어있다.
public delegate void Action();
public delegate void Action<in T>(T arg);
public delegate void Action<in T1,in T2>(T1 arg1, T2 arg2);
....
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T1, out TResult>();
public delegate TResult Func<in T1, in T2, out TResult>();
...
편리해지기 위해 사용하는 람다 식인데 오히려 delegate를 선언해야 하는 그러한 모순을
Action과 Func로 타파할 수 있다.
Action delegate
delegate의 원형을 보면 알겠지만 반환형이 void이다.
즉 결과를 반환하는 함수가 아닌 그저 실행이 목적이 함수이다.
제네릭 안에 들어가 있는건 파라미터의 type이다.
사용 예시를 보자
using System;
class Test
{
static void Main()
{
Action myAction = () => Console.WriteLine("Hello");
Action<int> myAction2 = (x) => Console.WriteLine($"input number is {x}");
Action<int, int> myAction3 = (a,b) => Console.WriteLine($"{a} * {b} = {a*b}");
myAction(); // Hello
myAction2(4); // input number is 4
myAction3(3,4); // 3*4 = 12
}
}
상당히 직관적이라 이해가 쉽다.
Func delegate
Action과 반대로 Func는 반환형이 존재하게 된다.
결과를 반드시 내놓아야 한다는 뜻이다.
delegate의 반환형은 제네릭의 맨 오른쪽 끝인 TResult가 정의해준다.
사용 예시를 보자
using System;
class Test
{
static void Main()
{
Func<int> myFunc = () => 10;
Func<int, int> myFunc2 = (x) => x*x;
Func<int, int, int> myFunc3 = (a, b) => a + b;
Console.WriteLine(myFunc()); // 10
Console.WriteLine(myFunc2(4)); // 16
Console.WriteLine(myFunc3(3,4)); // 7
}
}
마지막으로 함수 자체에 람다 식을 대입할 수 있다.
using System;
class Test
{
int number;
static void Main()
{
Print("Hello");
Console.WriteLine(GetNumber());
}
static void Print(string s) => Console.WriteLine(s);
static int GetNumber() => number;
}
람다 식은 왜 쓰는걸까?
익명메소드의 사용이유와 같은데, 그래도 써보자면
1.한번만 사용될 함수를 굳이 정의하지 않기 위해서
- 코드의 간결성을 높여준다
2.파라미터에 함수 그대로를 넘기고 싶어서
- C++의 함수 포인터처럼 함수를 파라미터화 시킬 수 있다.
'etc > C#' 카테고리의 다른 글
[C#] Lambda Function Capture (1) | 2021.07.30 |
---|---|
[C#] switch 제어문 (0) | 2021.07.16 |
[C#] 문자열 보간(Interpolation) (0) | 2021.07.15 |
[C#] Property(프로퍼티), C#만의 특별한 기능 (0) | 2021.07.13 |
[C#] Null-Safety를 지원하는 C# (0) | 2021.07.13 |