람다식은 알론조 처치라는 수학자가 1936년에 발표한 람다 계산법에서 사용하는 식을 제자 존 매커시가 이것을 프로그래밍 언어에 도입하여 만들었습니다.
람다식은 간결한 방법으로 함수를 묘사하기 위해 만들어졌습니다. 우리가 공부하는 C#,C++,자바,파이썬과 같은 주류 프로그래밍 언어는 대부분 람다식을 지원하고 있습니다.
람다식은 익명 메소드를 만들기 위해 사용합니다.
다만, 람다식으로 만드는 익명 메소드는 무명 함수라는 이름으로 부릅니다.
메소드는 입력(매개변수)과 출력(반환값)을 가지고 있죠? 람다식도 마찬가지입니다.
매개변수_목록 => 식
=> 연산자의 이름은 입력 연산자입니다.
이 연산자가 하는 역할은 그저 매개변수를 전달하는 것뿐입니다.
다음은 람다식의 선언 예입니다.
delegate int Calculate(int a, int b); //익명메소드를 만들려면 대리자가 필요합니다.
//...
static void Main(string[] args)
{
Calculate clac = (int a, int b) => a + b; //두 개의 int형식 매개변수 a,b를 받아
//이둘을 더해 반환하는 익명 메소드를 람다식으로 만들었습니다.
}
또한 C# 컴파일러는 코드를 더욱 간결하게 만들수 있도록 "형식 유추(type inference)"라는 기능을 제공합니다.
형식 유추를 사용하면 앞에 나온 예제 코드의 람다식에서 매개변수의 형식을 제거할 수 있습니다.
delegate int Calculate(int a, int b);
//...
static void Main(string[] args)
{
Calculate calc = (a,b) => a + b; //C#컴파일러는 Calculate 대리자의 선언 코드로 부터
//이 람다식이 만드는 익명 메소드의 매개변수의 형식을 유추해냅니다.
}
다음은 람다식의 예제 프로그램입니다.
using System;
namespace SimpleLamda
{
class MainApp
{
delegate int Calculate(int a, int b);
static void Main(string[] args)
{
Calculate calc = (a, b) => a + b;
Console.WriteLine($"3 + 4 : {calc(3, 4)}");
}
}
}
출력
3 + 4 : 7
앞에서 살펴본 람다식은 말 그대로 "식(Expression)" 형식을 하고 있었습니다.
문(Statement) 형식의 람다는 => 연산자 오른편에 식 형식 대신 코드 블록 {} 이 위치합니다.

문 형식의 람다는 if(a == b) return 0;나 Console.WriteLine(); 같은 문장들을 사용하기 위해 사용합니다.
문 형식의 람다 예제 코드입니다.
이 예제에서는 반환 형식과 매개변수가 없는 대리자를 사용하고 있습니다. 식 형식의 람다로는 반환 형식이 없는 무명 함수를 만들 수 없지만 문 형식의 람다를 이용하면 가능합니다.
delegate void DoSomething(); //void 형식으로 반환값이 없는 대리자를 만듭니다.
static void Main(string[] args)
{
DoSomthing Doit = () => //매개변수가 없는 경우는 아무것도 안넣습니다.
{
Console.WriteLine("뭔가를 ");
Console.WriteLine("출력해보자 ");
Console.WriteLine("이렇게");
};
Doit();
}
문 형식의 람다 예제 코드입니다.
문장을 입력하면 스페이스를 모두 지우는 프로그램입니다.
namespace StatementLambda
{
class MainApp
{
delegate string Concatenate(string[] args);
static void Main()
{
Concatenate concat =
(arr) =>
{
string result = "";
foreach (string s in arr)
result += s;
return result.Replace(" ", string.Empty);
};
string[] args = { "아버지가 방에 들어 가신다.", "할머니가 죽을 잡수신다." };
Console.WriteLine(concat(args));
}
}
}
출력
아버지가방에들어가신다.할머니가죽을잡수신다.
익명 메소드와 무명 함수는 코드를 더 간결하게 만들어주는 요소이나, 대부분 단 하나의 익명 메소드나 무명 함수를 만들기 위해 매번 별개의 대리자를 선언해야하기 때문에 번거로운 부분이 있습니다.
이것을 해결하기 위해 .NET에 Func와 Action 대리자가 미리 선언 되어있습니다.
Func와 Action은 .Net 라이브러리에 사전 정의 되어있는 대리자이다.
익명메소드, 무명 함수 정의를 위해 매번 대리자를 새롭게 정의하는 불편을 제거하기 위함이다.
일반화와 최대 16개의 매개변수를 지원한다.
Func 대리자 : 반환값이 있는 익명 메소드 / 무명 함수용 대리자
Action 대리자 : 반환값이 없는 익명 메소드 / 무명 함수용 대리자
Func 대리자는 결과를 반환하는 메소드를 참조하기 위해 만들어 졌으며, 17개의 메소드가 오버로딩 되어있습니다.

모든 Func 대리자의 형식 매개변수 중 가장 마지막에 있는 것(out)이 반환 형식입니다. (형식 매개변수가 하나뿐인 Func는 그 하나가 반환 형식입니다.)
다음은 입력매개 변수가 없는 버전,Func< TResult >의 사용예 입니다.
Func<int> func1 = () => 10; // 입력매개변수는 없으며, 무조건 10을 반환.
Console.WriteLine(func1()); // 10 출력
이번에는 매개변수가 하나 있는 버전,Func<T1,TResult>의 사용예입니다.
Func<int,int> Func2 = (x) => x*2; // 입력 매개변수는 int 형식 하나, 반환 형식도 int
Console.WriteLin(func2(3)); // 6을 출력
마지막으로 Func<T1,T2,TResult>의 사용예입니다.
Func<int,int,int> func3 = (x,y) => x+y; // 입력 매개변수는 int 형식 둘, 반환 형식은 int
Console.WriteLin(func2(2,3)); // 5를 출력
다음은 Func 대리자의 예제 프로그램입니다.
namespace FuncTest
{
class MainApp
{
static void Main(string[] args)
{
Func<int> func1 = () => 10;
Console.WriteLine($"func1() : {func1()}");
Func<int,int> Func2 = (x) => x * 2;
Console.WriteLine($"func2(4) : {func1(4)}");
Func<int,int,int> Func3 = (x,y) => x / y;
Console.WriteLine($"func2(22,7) : {func1(22,7)}");
}
}
}
출력
func1() : 10
func2(4) : 8
func2(22,7) : 3.142857142857143
Action 대리자는 반환 형식이 없습니다.
Action 대리자도 17개의 버전이 오버로딩 되어있습니다.

Action대리자의 형식 매개변수는 모두 입력 매개변수를 위해 선언되어 있습니다. Func와 달리 어떤 결과를 반환하는 것을 목적으로 하지 않고, 일련의 작업을 수행하는 것이 목적이기 때문입니다.
먼저 매개변수가 없는 Action의 사용예입니다.
Action act1 = () => Console.WriteLine("Action()");
act1();
다음은 매개변수가 하나뿐인 버전,Action< T >의 사용 예입니다.
int result = 0;
Action<int> act2 = (x) => result = x * x; // 람다식 밖에서 선언한 result에 x*x의 결과를 저장합니다.
act2(3);
Console.WriteLine($"result : {result}"); // 9를 출력
마지막으로 매개변수가 두개인 Action<T1,T2> 대리자의 사용 예입니다.
Action<double,double> act3 = (x,y) =>
{
double pi = x / y;
Console.WriteLine("Action<T1,T2>({0},{1}) : {2}" x, y, pi);
};
act3(22.0, 7.0);
Action대리자의 예제 프로그램입니다.
namespace ActionTest
{
class MainApp
{
static void Main(string[] args)
{
Action act1 = () => Console.WriteLine("Action()");
act1();
int result = 0;
Action<int> act2 = (x) => result = x * x;
act2(3);
Console.WriteLine($"result : {result}");
Action<double, double> act3 = (x, y) =>
{
double pi = x / y;
Console.WriteLine($"Action<T1,T2>({x},{y}) : {pi}");
};
act3(22.0, 7.0);
}
}
}
출력
Action()
result : 9
Action<T1,T2>(22,7) : 3.142857142857143
14.5장은 다음장에 따로 작성하였습니다.
메소드를 비롯하여 속성(인덱서), 생성자, 종료자는 공통된 특징이 있습니다. 이들은 모두 클래스의 멤버로서 본문이 중괄호{}로 만들어져 있습니다.
이러한 멤버의 본문을 식(Expression)만으로 구현 할 수있는데, 이렇게 식으로 구현된 멤버를 "식 본문 멤버(Exression-Bodied Member)" 라고 합니다.
식 본문 멤버의 문법은 다음과 같습니다.
멤버 => 식;
예제 코드를 작성해 보겠습니다.
일단 다음과 같이 필드 하나를 가지는 FriendList라는 클래스를 선언하겠습니다.
class FriendList
{
private List<string> list = new List<string>();
//여기에 나머지 멤버 구현
}
이제 FriendList 클래스에 식으로 이루어진 멤버를 하나씩 추가해 보겠습니다.
먼저 메소드 입니다.
//Add()와 Remove() 메소드는 각각
//list.Add()메소드를 호출하는 식과
//list.Remove()를 호출하는 식으로 이루어져 있습니다.
class FriendList
{
//...
public void Add(string name) => list.Add(name);
public void Remove(string name) => list.Remove(name);
}
생성자와 종료자도 식으로 구현해보겠습니다.
class FriendList
{
//...
public FriendList() => Console.WriteLine("FriendList() 생성자");
~FriendList() => Console.WriteLine("~FriendList() 종료자");
이번엔 읽기 전용 속성과 인덱서를 식으로 구현하는 예제입니다.
//get 키워드를 생략 가능합니다.
class FriendList
{
//...
//Capacity는 list의 용량입니다.
//리스트의 용량은 만든직후는 0이지만 한 개라도 추가하는순간 4가되고,
//용량을 초과하는 순간 두배씩 늘어납니다.
public int Capacity => list.Capacity(); //읽기전용 속성
public string this[int index] => list[index]; //읽기 전용 인덱서
}
읽기/쓰기 모두 가능한 속성 또는 인덱서를 구현해 보겠습니다.
//읽기 전용일 때는 생략이 가능했던 get(set) 키워드도 명시적으로 기술해 줘야합니다.
class FriendList
{
//...
public int Capacity //속성
{
get => list.Capacity;
set => list.Capacity = value;
}
public string this[int index] //인덱서
{
get => list[index];
set => list[index] = value;
}
}
지금까지 살펴봤던 예제를 실행 가능한 프로그램으로 만들어 테스트 해보겠습니다.
namespace _14._6_ExpressionBodiedMember
{
class FriendList
{
private List<string> list = new List<string>();
public void Add(string name) => list.Add(name);
public void Remove(string name) => list.Remove(name);
public void PrintAll()
{
foreach(var s in list)
Console.WriteLine(s);
}
public FriendList() => Console.WriteLine("Friendlist 생성자");
~FriendList() => Console.WriteLine("Friendlist 종료자");
//public int Capacity => list.Capacity; //읽기 전용 속성
public int Capacity //속성
{
get => list.Capacity;
set => list.Capacity = value;
}
//public string this[int index] => list[index]; //읽기 전용 인덱서
public string this[int index] //인덱서
{
get => list[index];
set => list[index] = value;
}
}
class MainApp
{
static void Main(string[] args)
{
FriendList obj = new FriendList();
Console.WriteLine($"obj.Capacity : {obj.Capacity}");
obj.Add("Tom");
obj.Add("Noah");
obj.Add("Jackson");
obj.Add("Lucas");
obj.Remove("Noah");
obj.PrintAll();
Console.WriteLine();
Console.WriteLine($"obj.Capacity : {obj.Capacity}");
obj.Capacity = 10;
Console.WriteLine($"obj.Capacity : {obj.Capacity}");
Console.WriteLine();
Console.WriteLine($"obj[0] : {obj[0]}");
Console.WriteLine();
obj[0] = "Aiden";
obj.PrintAll();
}
}
}
출력
Friendlist 생성자
obj.Capacity : 0
Tom
Jackson
Lucas
obj.Capacity : 4
obj.Capacity : 10
obj[0] : Tom
Aiden
Jackson
Lucas