익명 함수(이름이 없는 메서드)를 작성하는 간결한 방법
람다식을 사용하기 위해선 delegation이 필요하지만 delegation 함수는 사용하지 않아도 된다.
delegation : 특정 함수를 어딘가에 보관하여 보관된 함수를 호출하는 역할
형식 ( ) =? { };
예시
① (int a, int b) => { return a + b; }
② (int a, int b) => { a + b; }
③ (a, b) => a + b;
⇒ 대체로 ③의 형태로 사용한다.
delegate int Calculate(int a, int b);
static int add(int n1, int n2)
{
return n1 + n2;
}
static void Main(string [] args)
{
Calculate calc1 = add; //add메서드를 호출한다.
Console.WriteLine("calc1 : {0}", calc1(10, 20));
Calculate calc2 = (int a, int b) => a + b;
Console.WriteLine("calc2 : {0}", calc2(10, 20));
}
문장 형식의 람다식
실행할 코드를 중괄호로 묶어 여러 문장을 포함할 수 있다.
단일 표현식이 아닌 여러 동작을 수행해야 할 때 유용하다.
delegate void DoSomething();
static void Main(string [] args)
{
DoSomething Dolt = () =>
{
Console.WriteLine("출력1");
Console.WriteLine("출력2");
};
Dolt();
}
익명 메서드를 람다식으로 변환 ①
static void ThreadFunc(object obj)
{
Console.WriteLine("이름이 있는 메서드");
}
static void Main(string [] args)
{
//1. 이름이 있는 메서드
Therad thread1 = new Thread(ThreadFunc);
thread1.Start();
//2. 이름이 없는 메서드
Thread thread2 = new Thread(
delegate (object obj)
{
Console.WriteLine("이름이 없는 메서드");
});
thread2.Start();
//3. 람다식을 이용한 메서드
Thread thread3 = new Thread(
(obj) =>
{
Console.WriteLine("람다식을 이용한 메서드");
});
thread3.Start();
}
실행 결과 ✔
이름이 있는 메서드
이름이 없는 메서드
람다식을 이용한 메서드
이름이 없는 메서드를 사용할 땐 delegation 함수를 작성한다.
람다식을 이용할 땐 delegation은 작성하되, delegation 함수는 작성하지 않아도 된다. (바로 람다식으로 표현한다.)
익명 메서드를 람다식으로 변환 ②
delegate int ? MyDivide(int a, int b);
static void Main(string [] args)
{
MyDivide myDivide = (a, b) =>
{
if (b == 0) return null;
return a / b;
};
Console.WriteLine("10 / 2 == {0}", myDivide(10, 2));
COnsole.WriteLine("10 / 0 == {0}", myDivide(10, 0));
}
return문과 중괄호를 생략해도 된다.
delegate int MyAdd(int a, int b);
static void Main(string [] args)
{
MyAdd myAdd = (a, b) => a + b;
Console.WriteLine("10 + 2 == {0}", myAdd(10, 2));
}
MyDivide myDivide = (a, b) => a / b;로 바꾸어 쓸 수 있다. (조건문이 없을 때)위의 방식에서는 하나의 익명 메소드를 만들기 위해 매번 delegate를 따로 선언해줘야 하는 불편함이 있다.
이를 해결하기 위해서는 두 가지의 방식이 있다.
① Func delegate : 반환 값이 있다.
② Action delegate : 반환 값이 없다.
즉, 반환 값이 있는 무명 함수를 쓰고 싶을 땐 Func 방식을 사용하고, 반환 값이 없는 무명 함수를 사용하고 싶을 땐 Action 방식을 사용한다.
① Func 사용 예
반환 형식이 있다.
static void Main(string [] args)
{
//1. 입력매개변수 : 0개 / 반환 타입 : int
Func<int> func1 = () => 10;
Console.WriteLine("func1() : {0}", func1());
//2. 입력매개변수 : 1개, int형 / 반환 타입 : int
Func <int, int> func2 = (x) => x * 2;
Console.WriteLine("func2(4) : {0}", func2(4));
//3. 입력매개변수 : 2개, int형 / 반환 타입 : double
Func<double, double, double> func3 = (x, y) => x / y;
Console.WriteLine("func3(22, 7) : {0}", func3(22, 7));
}
실행 결과 ✔
func1() : 10
func2(4) : 8
fun3(22, 7) : 3.14285714285714
Func 키워드의 꺽쇠 안에 ⑴ 입력 매개 변수의 타입을 그 개수에 맞게 적은 다음 ⑵ 반환할 타입(1개)을 마지막에 적는다.
② Action 사용 예
반환 형식이 없다.
static void Main(string [] args)
{
//반환 타입 : 없음 / 입력매개변수 : 2개, double형
Action<double, double> action = (x, y) =>
{
double pi = x / y;
Console.WriteLine("Action<T1, T2>({0}, {1}) : {2}", x, y, pi);
}
action(22.0, 7.0);
}
실행 결과 ✔
Action<T1, T2>(22, 7) : 3.14285714285714
Action 키워드의 꺽쇠 안에 입력 매개 변수의 타입을 그 개수만큼 적는다.
연습
다양한 조건으로 검색이 가능한 메서드를 정의하고자 한다.
private static List<int> FilterOfInts(int[] source, Func<int, bool> filter)
{
List<int> result = new List<int>();
foreach (int i int source)
{
if (filter(i)) result.Add(i);
}
return result;
}
int 배열에서 여러 조건으로 필터링 할 수 있는 메서드
해당 값이 조건이 맞는지 확인해서 bool 형식을 반환하는 코드가 필요하므로 Func 방식을 사용하여 메서드를 정의하였다.
static void Main(string [] args)
{
int[] source = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// ---------------------- 홀수 필터 ----------------------
List<int> oddNumbers = FilterOfInts(source, (i => (i & 1) == 1));
//source 배열의 숫자가 홀수면 i(해당 숫자)를 oddNumbers 리스트에 추가
Console.WriteLine("----- 홀수 필터 -----");
foreach(var i in oddNumbers)
{
Console.Write(i+" "); //oddNumbers 리스트 요소들 출력
}
// ---------------------- 짝수 필터 ----------------------
List<int> evenNumbers = FilterOfInts(source, (i => (i & 1) == 0));
//source 배열의 숫자가 짝수면 i(해당 숫자)를 evenNumbers 리스트에 추가
Console.WriteLine("\n----- 짝수 필터 -----");
foreach(var i in evenNumbers)
{
Console.Write(i+" "); //evenNumbers 리스트 요소들 출력
}
}
실행 결과 ✔
----- 홀수 필터 -----
1 3 5 7 9
----- 짝수 필터 -----
2 4 6 8 10
(i => (i & 1) == 1)
i & 1 : 비트 연산자 &를 사용하여 i의 가장 오른쪽 비트인 1을 비교한다.
비트 연산은 i가 홀수일 경우 1을 반환하고, 짝수일 경우 0을 반환한다.
비교
delegation활용과Func방식의 비교
delegate string Concatenate(string [] str);
static void Exam1()
{
Console.WriteLine("[ 코드로서의 람다식(delegate) ]");
string[] stringArr = { "aaa", "bbb", "ccc", "ddd" };
Concatenate concatenate = (arr) =>
{
string result = "";
foreach(string s in arr)
{
result += s;
}
return result;
};
Console.WriteLine(concatenate(stringArr);
}
static void Exma2()
{
Console.WriteLine("[ Func 방식의 람다식 ]");
string[] stringArr = { "aaa", "bbb", "ccc", "ddd" };
Func<string[], string> concatenate = (arr) =>
{
string result = "";
foreach(string s in arr)
{
result += s;
}
return result;
}
Console.WriteLine(concatenate(stringArr);
}
실행 결과 ✔
[ 코드로서의 람다식(delegate) ]
aaabbbcccddd
[ Func 방식의 람다식 ]
aaabbbcccddd
Exam1()
: 함수 밖에 delegate을 생성하여 람다식을 작성하였다. 문자열 배열을 입력 받아 문자열 형태로 반환하고 있다.
Exam2()
: 마찬가지로 Func 방식을 사용하여 문자열 배열을 입력 받아 문자열 형태로 반환하고 있다.