1) 일반화(Generic)
다양한 변수타입을 하나로 묶는 방법
여러가지 변수에 대한 함수/클래스를 생성하지 않고, 하나에 대한 함수/클래스를 생성하여 모든 변수에서 사용할 수 있게 한다.
1-1) Object 변수
object obj = 3;
object obj2 = "hello world";
var obj3 = 3; //=int obj3 = 3;
var obj4 = "hello world"; //=string obj4 = "hello world";
int num = (int)obj;
string str = (string)obj2;
Q ) 모든 변수를 object 변수로 선언하는 것이 편하지 않은가?
object는 참조타입이기 때문에 느리다는 단점이 있다. 따라서 모든 변수를 object로 선언하는 것은 옳지 않다.
1-2) 클래스 일반화
object 타입을 이용하여 저장하는 방법도 느리기 때문에 문제가 있다. 따라서 일반화 방법을 사용한다.
class MyList<T>
{
T[] arr = new T[10];
//i번째 아이템을 리턴
public T GetItem(int i)
{
return arr[i];
}
}
class Monster { }
static void Main(string[] args)
{
MyList<int> myIntList = new MyList<int>();
MyList<float> myFloatList = new MyList<float>();
MyList<Monster> myMonsterList = newMyList<Monster>();
}
<>안에 특정한 이름을 적고(보통 Template의 약자인 ‘T’를 사용한다.) 그것을 클래스 안에서 쓰면 T의 위치에 어떤 타입을 넣어도 사용할 수 있게 된다.
1-3) 함수 일반화
static void Test<T>(T input){ }
static void Main(string[] args)
{
Test<int>(3);
Test<float>(3.0f);
}
함수명 옆에 <T>를 붙여주면 T의 위치에 어떤 변수를 사용하여도 상관 없다.
1-4) 인자가 여러개인 클래스 또는 함수의 일반화
class MyList<T, K> { }
static void Test<T, K>(T input1, K input2){ }
T 외의 다른 변수를 추가하면 된다.
2) 인터페이스(interface)
2-1) 추상클래스
자식클래스에서 오버라이딩하기를 강요한다.
abstract class Monster
{
public abstract virtual void Shout();
}
abstract class Flyable
{
public abstract void Fly();
}
class Orc : Monster
{
public override void Shout()
{
Console.WriteLine("어어어어!!");
}
}
class Skeleton : Monster
{
public override void Shout()
{
Console.WriteLine("끄어어어!!");
}
}
//메인함수
static void Main(string[] args)
{
//Monster monster = new Monster();
}
추상클래스는 new 클래스명();으로 생성할 수 없다.
abstract 클래스 안에서는 함수도 abstract로 만들 수 있다.
abstract함수는 추상함수이기 때문에 내용을 선언해 주면 안된다.
2-2) 인터페이스(Interface)
abstract class Flyable
{
public abstract void Fly();
}
class Orc : Monster
{
public override void Shout()
{
Console.WriteLine("어어어어!!");
}
}
//class FlyableOrc : Orc, Flyable { }
---------------------------------------------------
interface IFlyable //이름에 I붙여주는게 통상적임.
{
void Fly(); //리턴타입 함수명();만 적으면 된다.
}
class FlyableOrc : Orc, IFlyable
{
public void Fly(){}
}
//메인에서
static void Main(string[] args)
{
IFlyable flyable = new FlaybleOrc();
}
3) 프로퍼티(Property)
3-1) 프로퍼티
Get함수와 Set함수가 늘어남에 따라 조금 더 편리하게 해주기 위한 문법
class Knight
{
//기존의 방식
protected int hp;
public int getHp() { return hp; }
public void setHp() { this.hp = hp; }
//프로퍼티
protected int hp;
public int Hp
{
get { return hp; }
private set { this.hp = value; } //private로 설정
}
}
static void Main(string[] args)
{
Knight knight = new Knight();
knight.Hp = 100;
int hp = knight.Hp;
}
3-2) 자동완성 프로퍼티
class Knight
{
public int Hp { get; set; }
}
--------------
위의 코드는 아래와 같음
class Knight
{
privage int _hp;
public int GetHp() {return _hp;}
public void SetHp() {_hp = value;}
}
컴파일러가 자동으로 겹치지 않는 변수(_hp)를 만들어 줌
4) 대리자(Delegate)
4-1) 콜백함수
함수 자체를 인자로 넘겨주고, 필요로 할 때 함수를 호출하는 것
static void ButtonPressed(/* 함수 자체를 인자로 넘겨주고 */)
{
// 인자로 넘겨 받은 함수 호출();
}
delegate → 형식은 형식인데, 함수 자체를 인자로 넘겨주는 형식
delegate int OnClicked(); //OnClicked는 함수가 아닌, 자체가 int,float등과 같은 하나의 형식임.
// 반환 : int , 입력: void
// Onclicked이 delegate 형식의 이름
static void ButtonPressed(OnClicked clkickedFunction)
{
//필요할 때 함수 호출
clickedFunction();
}
static int TestDelegate()
{
Console.WriteLine("Hello Delegate");
return 0;
}
static int TestDelegate2()
{
Console.WriteLine("Hello Delegate2");
return 0;
}
//메인에서
ButtonPressed(TestDelegate);
//위 코드 한 줄은 다음과 같다.
OnCliked clicked = new OnCliked(TestDelegate);
cliked();
//Delegate끼리 합칠 수 있는 장점이 있음
OnCliked clicked = new OnCliked(TestDelegate);
cliked += TestDelegate2;
ButtonPressed(clicked);
5) 이벤트(Event)
위 코드에서 clicked();를 개나소나 부를 수 있다는 단점이 있다.
class InputManager
{
public delegate void OnInputKey();
public event OnInputKey InputKey;
public void Update()
{
//아무것도 입력하지 않음
if(Console.KeyAvailable == false)
return;
ConsoleKeyInfo info = Console.ReadKey();
if(info.Key == ConsoleKey.A)
{
//모두에게 알려준다!
InputKey();
}
}
}
class Program
{
static public void abc()
{
Console.WriteLine("abc");
}
static void Main(string[] args)
{
InputManager inputManager = new InputManager();
inputManager.InputKey += abc;
while (true)
{
inputManager.Update();
}
}
}
6) 람다식(Lambda)
6-1) 기존의 콜백함수
일회용 함수를 만드는데 사용하는 문법
enum ItemType
{
Weapon,
Armor,
Amulet,
Ring
}
enum Rarity
{
Normal,
Uncommon,
Rare
}
class Item
{
public ItemType ItemType;
public Rarity Rarity;
}
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 FindItem(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 item = FindItem(IsWeapon);
}
}
6-2) 무명함수
기존의 delegate를 쓰면 결국은 ItemSelector에 넣어줄 함수를 아이템의 종류마다 모두 구현해야 하는 것은 동일하다.
static List<Item> _items = new List<Item>();
delegate bool ItemSelector(Item item);
static Item FindItem(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 item = FindItem(delegate(Item item) { return item.ItemType == ItemType.Weapon; });
}
}
위와 같이 함수를 따로 만들지 않고, 메인에서 delegate를 쓰고 IsWeapon의 구현부를 그대로 가져와서 넣어주는 것이 무명함수이다.
6-3) 람다식
무명함수를 더 간략하게 표현
//무명함수
Item item = FindItem(delegate(Item item) { return item.ItemType == ItemType.Weapon; });
//람다식
Item item = FindItem((Item item) => { return item.ItemType == ItemType.Weapon; });
6-4) 더 간략하게
static List<Item> _items = new List<Item>();
delegate Return MyFunc<T, Return>(T item); //기존의 ItemSelector를 조금 더 공용적으로 대체
static Item FindItem(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> selector = (Item item) => return item.ItemType == ItemType.Weapon; });
Item item = FindItem(selector);
}
}
반환 타입이 있을 경우 Func를 사용하고, 반환 타입이 없으면 Action을 사용한다.
7) 예외 처리(Exception)
예외적인 상황에 대한 처리
7-1) 형식
try-catch 형식
try
{
//시도할 구문 > 예외 발생 시 catch구문 실행
int a = 10;
int b = 0;
int result = a/b;
}
catch(DivideByZeroException e)
{
Console.WriteLine("0으로 나누는 잘못된 시도를 하였습니다.");
}
catch(Exception e)
{
//먼저 있는 catch에서 잡히면 뒤의 catch는 실행되지 않는다.
Consol.WriteLine("알 수 없는 잘못된 시도입니다");
}
finally
{
//try-catch와 상관없이 무조건 실행되어야 하는 부분
//DB,파일 정리 등
}
7-2) 예외 종류
8) 리플렉션(Reflection)
8-1) 리플렉션
X-Ray를 찍는 것
class Monster
{
public int hp;
protected int attack;
private float speed;
void Attack() {}
}
static void Main(string[] args)
{
Monster monster = new Monster();
Type type = monster.GetType();
var fields = type.GetFields(System.Reflection.BindFlages.Public
|System.Reflection.BindFlages.NonPublic
|System.Reflection.BindFlages.Static
|System.Reflection.BindFlages.Instance);
foreach(FieldInfo field in fields)
{
string access = "protected";
if(field.IsPublic)
access = "public";
else if(field.IsPrivate)
access = "private";
Console.WriteLine($"{access} {field.FieldType.Name} {field.Name}");
}
}
8-2) Attribute
class Important : System.Attribute
{
string message;
public Important(string message) {this.message = message;}
}
class Monster
{
[Important("Very Important")]
public int hp;
protected int attack;
private float speed;
void Attack() {}
}
9) 널러블(Nullable)