C# 기타 문법

POSI·2022년 12월 9일
0

C#

목록 보기
5/6

해당 문서는 인프런 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part1: C# 기초 프로그래밍 입문을 듣고 정리한 필기 노트입니다.

Generic 일반화

일반적으로 쓸 수 있는 변수, object

→ 하지만 참조 타입이기 때문에 모든 변수들을 object로 선언하기는 어렵다

// object : 형식이 object (참조 타입)
// var : 컴파일러가 들어온 값을 보고 형식을 때려 맞춤
object obj = 3;
object obj2 = "hello world";

// 사용할 때는 casting을 해줘야한다
// 기본적으로 변수들은 object를 상속하고 있음
int num = (int) obj;
string str = (string) obj2;

Generic Class T

→ 매개변수 T가 모든 타입에서도 돌아가는 조커 키

// 클래스의 일반화
class MyList<T>
{
	T[] arr = new T[10];

	// 반환도 T로
	public T GetItem(int i)
	{
		return arr[i];
	}
}

// 값 여러개도 가능
class MyList<T, K>
{

}

// T에 조건 추가
class MyList<T> where T : class // 참조 형식이어야 함
{

}
class MyList<T> where T : struct // 값 형식이어야 함
{

}
class MyList<T> where T : new() // 어떠한 인자도 받지 않는 기본 생성자가 있어야 함
{

}
class MyList<T> where T : Monster // Monster 혹은 Monster를 상속받은 class 여야 함
{

}

// 함수의 일반화
static void Test<T>(T input)
{
	
}

static void main(string[] args)
{
	MyList<int> myIntList = new MyList<int>();
	MyList<short> myIntList = new MyList<short>();
	
	Test<int>(3);
	Test<float>(3.0f);
}

추상 클래스

클래스를 설계할 때 행위에 강요를 주는 문법
인스턴스를 만들 수 없고 추상적으로만 사용 가능

abstract class Monster // 추상 클래스
{
	public abstract void Shout(); // 추상 메소드
	// Monster를 상속받는 클래스들은 무조건 추상 메소드를 override 해야 함
	// Shout()을 무조건 사용해라 라는 강요
}

class Orc : Monster
{
	public override void Shout() 
	{
		
	}
}

Interface

다중 상속은 C#에서 금지!!
→ 인터페이스는 물려주지만 구현물은 물려주지 않는 새로운 문법 등장 “Interface”

interface IFlyable
{
	void Fly();
}

class Orc : Monster
{
	public override void Shout() 
	{
		
	}
}

class FlyableOrc : Orc, IFlyable
{
	public void Fly()
	{
		
	}
}

→ IIFlyable이라는 기능을 가지고 있는 애라면 Fly() 라는 인터페이스 함수를 제공해야한다.

응용

static void DoFly(IFlyable flyable)
{
	flyable.Fly();
}

static void Main(string[] args)
{
	IFlyable orc = new FlyableOrc();
	DoFly(orc);
}

Property 프로퍼티

get, set을 이용하여 은닉성을 유지하며 간편하게 가져오고, 설정할 수 있도록 해주는 문법

class Knight
{
	protected int hp;
	
	public int Hp
	{
		get { return hp; }
		set { hp = value; } // private 붙여도 되고, 여러 줄로 써도 됨
	}
}

static void Main(string[] args)
{
	Knight knight = new Knight();
	knight.Hp = 100; // value = 100
	
	int hp = knight.Hp;
}

자동 완성 프로퍼티

class Knight
{
	public int Hp { get; set; }

	// 바로 초기화도 가능
	public int Hp { get; set; } = 100;
}

위처럼만 써도 get, set 함수가 알아서 구현됨


Delegate 대리자

함수 자체를 인자로 넘겨주는 방식

static void ButtonPressed(/* 함수 자체를 인자로 넘겨주고 */)
{
	// 함수를 호출();
}

static void Main(string[] args)
{
	ButtonPressed(/* */);
}
delegate int OnClicked();
// delegate -> 형식은 형식인데, 함수 자체를 인자로 넣어주는 형식
// 반환: int, 입력: void
// OnClicked 가 이 delegate 형식의 이름

static void ButtonPressed(OnClicked clickedFunction)
{
	clickedFunction();
}

static int TestDelegated()
{
	Console.WriteLine("Hello Delegate");
}

static int TestDelegated2()
{
	Console.WriteLine("Hello Delegate 2");
}

static void Main(string[] args)
{
	ButtonPressed(TestDelegate);

	// chaining
	OnClicked clicked = new OnClicked(TestDelegate);
	clicked += TestDelegate2;
	
	ButtonPressed(clicked);
}

Event 이벤트

Input 정보를 뿌리는 코드

delegate int OnClicked();
// delegat -> 형식은 형식인데, 함수 자체를 인자로 넣어주는 형식
// 반환: int, 입력: void
// OnClicked 가 이 delegate 형식의 이름

static void ButtonPressed(OnClicked clickedFunction)
{
	clickedFunction();
}

static int TestDelegated()
{
	Console.WriteLine("Hello Delegate");
}

static int TestDelegated2()
{
	Console.WriteLine("Hello Delegate 2");
}

static void Main(string[] args)
{
	ButtonPressed(TestDelegate);

	// chaining
	OnClicked clicked = new OnClicked(TestDelegate);
	clicked += TestDelegate2;
	
	ButtonPressed(clicked);
}
class Program
{
	static void OnInputTest()
	{
		Console.WriteLine("Input Received!");
	}

	static void Main(string[] args)
	{
		InputManager inputManager = new InputManager();
		inputManager.InputKey += OnInputTest;

		while(true)
		{
			inputManager.Update();
		}
	}
}

event에 기능을 추가는 가능하지만 event 자체에는 직접 호출이 불가한 점이 delegate와의 차이점!


Lambda 람다식

일회용 함수를 만드는데 사용하는 문법

class Program
{
	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.Normal });
		_items.Add(new Item() { ItemType = ItemType.Armor, Rarity.Uncommon });
		_items.Add(new Item() { ItemType = ItemType.Ring, Rarity.Rare });

		// Anonymous Function
		// 방법 1
		Item item = FindItem(delegate (Item item) { return item.ItemType == ItemType.Weapon; });
		// 방법 2
		Item item = FindItem((Item item) => { return item.ItemType == ItemType.Weapon; });
	}
}
class Program
{
	static List<item> _items = new List<Item>();
	
	delegate Return MyFunc<T, Return>(T item);

	static Item FindItem(MyFunc<Item, bool> selector) ...

	static void Main(string[] args)
	{
		_items.Add(new Item() { ItemType = ItemType.Weapon, Rarity.Normal });
		_items.Add(new Item() { ItemType = ItemType.Armor, Rarity.Uncommon });
		_items.Add(new Item() { ItemType = ItemType.Ring, Rarity.Rare });

		MyFunc<Item, bool> selector = new MyFucn<Item, bool>((Item item) => { return item.ItemType == ItemType.Weapon; });
		Item item = FindItem(selector);

		// 참고 : new 안해도 됨
		MyFunc<Item, bool> selector = (Item item) => { return item.ItemType == ItemType.Weapon; };
	}
}

Func, Action

C#에서는 이런 애들이 인자 수에 따라 이미 만들어져 있어서 바로 사용 가능

	static void Main(string[] args)
	{
		_items.Add(new Item() { ItemType = ItemType.Weapon, Rarity.Normal });
		_items.Add(new Item() { ItemType = ItemType.Armor, Rarity.Uncommon });
		_items.Add(new Item() { ItemType = ItemType.Ring, Rarity.Rare });

		// Func : Return 있음
		Func<Item, bool> selector = (Item item) => { return item.ItemType == ItemType.Weapon; };
		// Action : return type이 없는 void 형
		Action<Item> selector = (Item item) => {  };
	}

Exception 예외 처리

  1. 0으로 나눌 때
  2. 잘못된 메모리를 참조 (null)
  3. 오버플로우
try
{
	int a = 10;
	int b = 0;
	int result = a / b;
}
catch (DivideByZeroException e)
{

}
catch (Exception e)
{
	
}
finally
{
	// 예외가 발생하더라도 실행
}

custom exception도 가능

class TestException : Exception
{
	
}

try
{
	throw new TestException();
}
catch (Exception e)
{
	
}

Reflection 리플렉션

X-Ray를 찍는 것 = class 내의 정보를 runtime에 불러올 수 있다

using System.Reflection;

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.BindingFlags.Public
			| System.Reflection.BindingFlags.NonPublic
			| System.Reflection.BindingFlags.Static
			| System.Reflection.BindingFlags.Instance);
	
	foreach (FieldInfo fields in fields)
	{
		string access = "protected";
		if (field.IsPublic)
			access = "public";
		else if (field.IsPrivate)
			access = "private";

		Console.WriteLine($"{access} {field.FieldType.Name}");
	}
}

attribute

runtime 중에서도 참고할 수 있는 주석

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

Nullable

= Null + able

null도 될 수 있다는 표시로 ? 추가

int? number = null
static int Find()
{
	return 0;
}

static void Main(string[] args)
{
	int? number = null;
	number = 3;

	int a = number.Value; // Nullable과 int는 변환 불가. Value를 꺼내 써야함
}

null인 지 아닌 지를 늘 체크해야 함

int? number = null;

if (number != null)
{
	int a = number.Value;
	Console.WriteLine(a);
}

if (number.HasValue)
{
	int a = number.Value;
	Console.WriteLine(a);
}

?? 문법

int b = number ?? 0;

number 가 null인 지 아닌 지를 확인한 다음에 만약
→ null이라면 초기값(0)을 넣어주고
→ null이 아니라면 b에 number의 value를 넣어줌

class에 적용 ?.

class Monster
{
	public int Id { get; set; }
}

static void Main(string[] args)
{
	Monster monster = null;
	
	int? id = monster?.Id;
}

monster가 null이 아니라면 Id를 받아옴.
없으면 null
→ null 형식으로 받아와야함

profile
고양이가 운영하는 테크블로그

0개의 댓글