# Lambda 개념
일회용 함수를 만드는 문법이다
# Lambda 왜 필요한가?
- 일반적인 하드코딩 방법
아래의 코드를 보면, Find 함수를 종류별로 다 만들어주며
매우 비효율적이다
using System;
namespace CSharp
{
enum ItemType
{
Weapon,
Armor,
Amulet,
Ring,
}
enum Rarity
{
Normal,
Uncommon,
Rare,
}
class Item
{
public ItemType ItemType;
public Rarity Rarity;
}
internal class Program
{
static List<Item> _items = new List<Item>();
delegate bool ItemSelector(Item item);
static Item FindWeapon()
{
foreach (Item item in _items)
{
if(item.ItemType == ItemType.Weapon)
return item;
}
return null;
}
static Item FindArmor()
{
...
}
static Item FindWeapon()
{
...
}
static Item FindRing()
{
...
}
static void Main(string[] args)
{
_items.Add(new Item() { ItemType = ItemType.Weapon, Rarity = Rarity.Normal });
_items.Add(new Item() { ItemType = ItemType.Armor, Rarity = Rarity.Uncommon });
_items.Add(new Item() { ItemType = ItemType.Ring, Rarity = Rarity.Rare });
Item item1 = FindWeapon();
}
}
}
- 중복되는 부분을 해결(개선)하는 방법
delegate를 이용하면 된다
using System;
namespace CSharp
{
enum ItemType
{
Weapon,
Armor,
Amulet,
Ring,
}
enum Rarity
{
Normal,
Uncommon,
Rare,
}
class Item
{
public ItemType ItemType;
public Rarity Rarity;
}
internal class Program
{
static List<Item> _items = new List<Item>();
delegate bool ItemSelector(Item item);
static bool IsWeapon(Item item)
{
return item.ItemType == ItemType.Weapon;
}
static Item FindWeaponWithDelegate(ItemSelector selector)
{
foreach (Item item in _items)
{
if (selector(item))
return item;
}
return null;
}
static void Main(string[] args)
{
_items.Add(new Item() { ItemType = ItemType.Weapon, Rarity = Rarity.Normal });
_items.Add(new Item() { ItemType = ItemType.Armor, Rarity = Rarity.Uncommon });
_items.Add(new Item() { ItemType = ItemType.Ring, Rarity = Rarity.Rare });
Item item2 = FindWeaponWithDelegate(IsWeapon);
}
}
}
- 일회성으로 사용하기 위한 방법
위와 같이 delegate를 사용해 만드는 경우에
항상 함수를 선언 및 구현을 해야 한다는 아쉬운 점이 있다
그래서 일회성으로 사용할 함수를 만드려면
아래와 같이 작성을 하면 되고 이를 Anonymous Function이라고 한다
using System;
namespace CSharp
{
enum ItemType
{
Weapon,
Armor,
Amulet,
Ring,
}
enum Rarity
{
Normal,
Uncommon,
Rare,
}
class Item
{
public ItemType ItemType;
public Rarity Rarity;
}
internal class Program
{
static List<Item> _items = new List<Item>();
delegate bool ItemSelector(Item item);
static Item FindWeaponWithDelegate(ItemSelector selector)
{
foreach (Item item in _items)
{
if (selector(item))
return item;
}
return null;
}
static void Main(string[] args)
{
_items.Add(new Item() { ItemType = ItemType.Weapon, Rarity = Rarity.Normal });
_items.Add(new Item() { ItemType = ItemType.Armor, Rarity = Rarity.Uncommon });
_items.Add(new Item() { ItemType = ItemType.Ring, Rarity = Rarity.Rare });
Item item3_1 = FindWeaponWithDelegate(delegate (Item item)
{
return item.ItemType == ItemType.Weapon;
});
// 재사용하려고 객체를 따로 뺀 버전은 아래와 같다
ItemSelector selector = new ItemSelector(delegate (Item item)
{
return item.ItemType == ItemType.Weapon;
});
Item item3_2 = FindWeaponWithDelegate(selector);
}
}
}
- 람다함수를 사용하는 방법
delegate를 사용하는 Anonymous Function이
다소 직관적이지 않다고 생각했는지 아래와 같은 문법을 새로 만들었고
이를 람다함수라고 한다
// 람다함수
Item item4 = FindWeaponWithDelegate((Item item) =>
{
return item.ItemType == ItemType.Weapon;
});
// 재사용하는 버전 1
ItemSelector lambda1 = new ItemSelector((Item item) =>
{
return item.ItemType == ItemType.Weapon;
});
Item item5 = FindWeaponWithDelegate(lambda1);
// 재사용하는 버전 2
ItemSelector lambda2 = (Item item) =>
{
return item.ItemType == ItemType.Weapon;
};
Item item6 = FindWeaponWithDelegate(lambda2);
# 확장성 높여보자!
반환 타입과 인자로 받을 타입 각각 하나씩
Generic을 활용해서 확장성 높일 수 있다
using System;
namespace CSharp
{
enum ItemType
{
Weapon,
Armor,
Amulet,
Ring,
}
enum Rarity
{
Normal,
Uncommon,
Rare,
}
class Item
{
public ItemType ItemType;
public Rarity Rarity;
}
internal class Program
{
static List<Item> _items = new List<Item>();
delegate R MyFunc<T, R>(T item);
static Item FindWeaponWithGenericLambda(MyFunc<Item, bool> selector)
{
foreach (Item item in _items)
{
if (selector(item))
return item;
}
return null;
}
static void Main(string[] args)
{
_items.Add(new Item() { ItemType = ItemType.Weapon, Rarity = Rarity.Normal });
_items.Add(new Item() { ItemType = ItemType.Armor, Rarity = Rarity.Uncommon });
_items.Add(new Item() { ItemType = ItemType.Ring, Rarity = Rarity.Rare });
MyFunc<Item, bool> lambdaWithGeneric = (Item item) =>
{
return item.ItemType == ItemType.Weapon;
};
Item item7 = FindWeaponWithGenericLambda(lambdaWithGeneric);
}
}
}
# 확장성 더 높여보자!
위의 코드는 인자가 하나인 경우만 가능한 delegate이다
그렇기에 인자가 없거나 혹은 인자가 여러 개인 경우에 대한 확장성이 부족하다
이를 해결하려면, 다양한 버전을 만들어서 사용하면 된다
delegate R MyFunc0<R>();
delegate R MyFunc1<T, R>(T t);
delegate R MyFunc2<T, R>(T t1, T t2);
...
실제로는 작업을 할 때는
우리가 위처럼 delegate를 종류별로 직접 많이 만들어 줄 필요가 없다
C#에서는 위의 다양한 버전을 이미 다 만들어 놓았다
따라서 이러한 기능을 제공해 주는 Func를 활용하면 된다
참고로 Func는 항상 out TResult가 있기에
void를 리턴하는 함수를 만들고 싶다면, Func가 아닌 Action을 활용하면 된다
using System;
namespace CSharp
{
enum ItemType
{
Weapon,
Armor,
Amulet,
Ring,
}
enum Rarity
{
Normal,
Uncommon,
Rare,
}
class Item
{
public ItemType ItemType;
public Rarity Rarity;
}
internal class Program
{
static List<Item> _items = new List<Item>();
// delegate R MyFunc<T, R>(T item); // 굳이 만들어 줄 필요 X
static Item FindWeaponWithFunc(Func<Item, bool> selector)
{
foreach (Item item in _items)
{
if (selector(item))
return item;
}
return null;
}
static void Main(string[] args)
{
_items.Add(new Item() { ItemType = ItemType.Weapon, Rarity = Rarity.Normal });
_items.Add(new Item() { ItemType = ItemType.Armor, Rarity = Rarity.Uncommon });
_items.Add(new Item() { ItemType = ItemType.Ring, Rarity = Rarity.Rare });
Func<Item, bool> lambdaWithFunc = (Item item) =>
{
return item.ItemType == ItemType.Weapon;
};
Item item8 = FindWeaponWithFunc(lambdaWithFunc);
}
}
}
# 결론
delegate를 직접 선언할 이유가 없다
아래 코드처럼 1번 방식을 사용하면 된다
만약, delegate(Func) 객체를 저장해야 한다면
2번 방식을 사용하면 된다
주의점은 반환타입 여부에 따라서 Func vs Action을 선택을 해야한다
반환 타입 있는 경우: Func
반환 타입 없는 경우: Action
using System;
namespace CSharp
{
enum ItemType
{
Weapon,
Armor,
Amulet,
Ring,
}
enum Rarity
{
Normal,
Uncommon,
Rare,
}
class Item
{
public ItemType ItemType;
public Rarity Rarity;
}
internal class Program
{
static List<Item> _items = new List<Item>();
static Item FindWeaponWithFunc(Func<Item, bool> selector)
{
foreach (Item item in _items)
{
if (selector(item))
return item;
}
return null;
}
static void Main(string[] args)
{
_items.Add(new Item() { ItemType = ItemType.Weapon, Rarity = Rarity.Normal });
_items.Add(new Item() { ItemType = ItemType.Armor, Rarity = Rarity.Uncommon });
_items.Add(new Item() { ItemType = ItemType.Ring, Rarity = Rarity.Rare });
// 1번 방식: 람다함수 재사용이 필요하지 않아 임시적으로 사용하는 경우
Item item1 = FindWeaponWithFunc((Item item) =>
{
return item.ItemType == ItemType.Weapon;
});
// 2번 방식: 람다함수 재사용이 필요해 저장하는 경우
Func<Item, bool> lambdaWithFunc = (Item item) =>
{
return item.ItemType == ItemType.Weapon;
};
Item item2 = FindWeaponWithFunc(lambdaWithFunc);
}
}
}
'C# > [루키스] 실전 문법' 카테고리의 다른 글
| [C# 섹션 8] Reflection (0) | 2025.06.26 |
|---|---|
| [C# 섹션 8] Exception (2) | 2025.06.26 |
| [C# 섹션 8] Event (0) | 2025.06.25 |
| [C# 섹션 8] Delegate (0) | 2025.06.25 |
| [C# 섹션 8] Property (0) | 2025.06.24 |