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();
}

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) 예외 종류

  1. 0으로 나눈 경우
  2. 잘못된 메모리를 참조한 경우(null)
  3. 오버플로우

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)