해당 문서는 인프런 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part1: C# 기초 프로그래밍 입문을 듣고 정리한 필기 노트입니다.
→ 하지만 참조 타입이기 때문에 모든 변수들을 object로 선언하기는 어렵다
// object : 형식이 object (참조 타입)
// var : 컴파일러가 들어온 값을 보고 형식을 때려 맞춤
object obj = 3;
object obj2 = "hello world";
// 사용할 때는 casting을 해줘야한다
// 기본적으로 변수들은 object를 상속하고 있음
int num = (int) obj;
string str = (string) obj2;
→ 매개변수 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()
{
}
}
다중 상속은 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);
}
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 함수가 알아서 구현됨
함수 자체를 인자로 넘겨주는 방식
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);
}
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와의 차이점!
일회용 함수를 만드는데 사용하는 문법
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; };
}
}
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) => { };
}
try
{
int a = 10;
int b = 0;
int result = a / b;
}
catch (DivideByZeroException e)
{
}
catch (Exception e)
{
}
finally
{
// 예외가 발생하더라도 실행
}
class TestException : Exception
{
}
try
{
throw new TestException();
}
catch (Exception e)
{
}
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}");
}
}
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() { }
}
= Null + able
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를 꺼내 써야함
}
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 Monster
{
public int Id { get; set; }
}
static void Main(string[] args)
{
Monster monster = null;
int? id = monster?.Id;
}
monster가 null이 아니라면 Id를 받아옴.
없으면 null
→ null 형식으로 받아와야함