0. 생성한 스크립트
- Structures : 구조체 스크립트
- Classes : 오버라이드 등 클래스 예제
- Expression : 프로퍼티 및 식 본문 멤버 사용 예시
- Inheritance : 상속 예시
- Generic_Func : 제네릭 함수 예시
- Generic_Stack : 제네릭 스택 생성
1. 프로퍼티
- 프로퍼티 : 클래스가 구현 또는 검증 코드를 숨기는 동시에 값을 가져오고 설정하는 방법을 공개적으로 노출할 수 있다.
- get 프로퍼티 : 속성 값을 반환하는 데 사용된다.
- set 프로퍼티 : 새 값을 할당하는 데 사용된다.
- 두 프로퍼티중 하나만 사용하여 읽기전용(get만 사용), 쓰기전용(set만 사용)으로 만들 수 있다.
- value 키워드 : set 또는 init 접근자가 할당하는 값을 정의하는 데 사용된다.
- 프로퍼티는 변수 바로 밑에 생성하는게 보기 편하다.
- 멤버 변수 => property => 멤버 함수
- get : 저장된 값을 쓴다. / set : 값을 저장한다.
private float mpBooster;
public float MpBooster
{
//get { return mpBooster; }
//set { mpBooster = value; }
get => mpBooster;
set => mpBooster = value;
}
2. 생성자
- 생성자 : 클래스 또는 구조체의 인스턴스가 만들어질 때마다 해당 생성자가 호출된다.
- 서로 다른 인수를 사용하는 여러 생성자가 있을 수 있다.
- 객체가 생성될때 자동으로 호출되는 함수
class Character
{
//생성자는 객체를 생성할때 반드시 호출되기 때문에 public으로 만들어야한다.
public Character() //기본 생성자는 특별한 경우가 아닌경우 사용하지 않는다. / 생성자는 오버로딩 가능
{
Debug.Log("생성자"); //모노비헤비어 상속이 안되어있는 클래스이기 때문에 print사용이 불가능하다.
}
//public Character(string name)
//{
// this.name = name;
//}
public Character(string name)
:this(name, 100, 100) //밑의 매개변수 3개인 생성자를 호출한다.(생성자 2개를 동시에 호출할때 사용한다.)
{
}
public Character(string name, float health, float magician)
{
this.name = name;
this.health = health;
this.magician = magician;
}
}
3. new 연산자
- new 연산자는 새 유형의 인스턴스를 만든다.
- 생성자 호출
- 배열 생성
- new 키워드를 멤버 선언 한정자 또는 제네릭 형식 제약 조건으로 사용할 수도 있다.
- var dict = new Dictionary<string, int> { };
Character player = new Character("Player"); //생성자 / Character() : 생성자를 호출하는 명령이다.

4. this.변수
- 클래스의 현재 생성자를 가리키며 확장 메서드의 첫 번째 매개 변수에 대한 한정자로도 사용된다.
- 클래스 내부의 private 접근지정자 변수에 this를 붙여서 사용할 수 있다.
- private string name; => this.name = name;
- 개체를 다른 메서드에 매개변수로 전달한다.
- CalcTax(this);
- 클래스 내부의 private 접근지정자 변수에 this를 붙여서 사용할 수 있다.
- 멤버변수 즉, 객체의 변수
public Character(string name, float health, float magician)
{
this.name = name;
this.health = health;
this.magician = magician;
}
5. 식 본문 멤버
- 식 본문 정의를 사용하면 간결하고 읽기 쉬운 형식으로 멤버의 구현을 제공할 수 있다.
- 메서드 또는 속성과 같은 지원되는 멤버에 대한 논리가 단일 식으로 구성된 경우 식 본문 정의를 사용할 수 있다.
- 식 본문 정의는 다음 형식 멤버와 함께 사용할 수 있다.
- Method
- public override string ToString() => $"{fname} {lname}".Trim();
- 읽기 전용 속성
- 속성
- get => mpBooster;
- 생성자
- public Character(string name) => Name = name;
- 종료자
- 인덱
- Method
- =>(return 기호다)
public float MpBooster
{
//get { return mpBooster; }
//set { mpBooster = value; }
get => mpBooster;
set => mpBooster = value;
}
6. static
- static 한정자를 사용하여 특정 개체가 아니라 형식 자체에 속하는 정적 멤버를 선언할 수 있다.
- static 한정자를 사용하여 static 클래스를 선언할 수 있다.
- 클래스, 인터페이스 및 구조체에서 필드, 메서드, 속성, 연산자, 이벤트 및 생성자에 static 한정자를 추가할 수 있다.
- static 키워드가 클래스에 적용된 경우 클래스의 모든 구성원은 static 이어야 한다.
- static 한정자를 로컬 함수에 추가할 수 있다.
- 정적 로컬 함수는 지역 변수 또는 인스턴스 상태를 캡처할 수 없다.
- 클래스 안에 모든 영역에서 공용으로 단 하나만 사용된다. (데이터영역에 저장된다.)
- static 생성자는 접근 지정자가 없고 기본적으로 public으로 생각해라
- 상속 불가능하다.
private static int count;

7. 오버라이드
- 상속된 메서드, 속성, 인덱서 또는 이벤트의 추상 또는 가상 구현을 확장하거나 수정하는 데 필요하다.
- 오버라이드 메서드는 기본 클래스에서 상속된 멤버의 새 구현을 제공한다.
- 정적 메서드는 재정의할 수 없다.
- 오버라이드 선언에서는 virtual 메서드의 액세스 가능성을 변경할 수 없다.
- 오버라이드 메서드 및 virtual 메서드 둘 다에 동일한 엑세스 수준 한정자(public, protected 등)가 있어야한다.
- 재정의된 기본 메서드는 virtual, abstraact, override여야한다.
public override string ToString()
{
return $"{id}, {name}, {health}, {magician}";
}

8. 상속
- 상속을 사용하면 다른 클래스에 정의된 동작을 다시 사용, 확장 및 수정하는 새 클래스를 만들 수 있다.
- 인터페이스 선언은 그 멤버의 기본 구현을 정의할 수 있다.
- 이러한 구현은 파생된 인터페이스에 의해, 그리고 해당 인터페이스를 구현하는 클래스에 의해 상속된다.
- 기본 클래스가 메서드를 virtual로 선언하는 경우 자식 클래스가 자체 구현으로 메서드를 어버라이드 할 수 이싿.
- 기본 클래스가 멤버를 abstract로 선언하는 경우 해당 클래스에서 직접 상속되는 모든 비추상 클래스에서 메서드를 재정의해야한다.
- 자식 클래스 자체가 abstract인 경우 직접 구현하지 않고 추상 멤버를 상속한다.
- 상속받은 클래스의 객체 안에 상속받은 부모 클래스의 공간이 따로 있고 그 안에 부모클래스의 변수 공간이 할당되어있다.
- 함수를 오버라이드하면 자신의 힙(heap)공간 안에 오버라이드한 함수가 따로 저장된다.
- 이때 부모의 원래의 함수 주소와, 오버라이드한 자식의 함수 주소가 가상의 테이블에 저장된다(가상 테이블)
- private 변수는 자식도 사용 못하지만, protected는 자식도 사용할 수 있다.
- 프로퍼티도 상속된다.
- A oba = new A(); / B obb = new B(); / A obb2 = (A) obb; => 업 캐스팅(이건 무조건 가능한 방법이다)
- A obb2 = (A) obb; 이렇게하면 B공간에 생성된 부모A 공간에 바로 접근이 가능해진다.
- C obc = new (C); / B obb3 = (B) obb2; (3교시 녹음 마지막부분 잘 듣자)
class Character
{
protected string name;
public string Name
{
get => name;
set => name = value;
}
private float hp;
public float Hp
{
get => hp;
set => hp = value;
}
public Character(string name, float hp)
{
this.name = name;
this.hp = hp;
}
public override string ToString() => $"{name}, {hp}";
public virtual void Move()
{
Debug.Log("오른쪽 방향으로 10만큼 이동");
}
}
class Player : Character
{
public Player()
: base("None", -1) //부모 생성자에 넘기는 인자
{
Debug.Log(ToString());
}
public Player(string name, float hp)
: base (name, hp)
{
Debug.Log(ToString());
}
public override void Move()
{
base.Move();
Debug.Log("전진 방향으로 10만큼 이동");
}
}
Player p = new Player();
Player p2 = new Player("Player2", 100);
p2.Move();


9. 가상화 (virtual 키워드)
- virtual 키워드는 메서드, 속성, 인덱서 또는 이벤트 선언을 수정하고 파생 클래스에서 재정의하도록 허용하는 데 사용된다.
public virtual void Move()
{
Debug.Log("오른쪽 방향으로 10만큼 이동");
}
public override void Move()
{
base.Move();
Debug.Log("전진 방향으로 10만큼 이동");
}

10. 업 캐스팅 / 다운 캐스팅
- 업 캐스팅 : 자식 객체에서 부모 객체로 형변환하는 것을 의미한다.
- 자식 타입의 객체를 부모 타입의 변수로 참조하는 것이다.
- 단, 자식 타입의 객체의 전부에 접근할 순 없고 부모로부터 상속받은 멤버들만 접근이 가능하다.
- 다운 캐스팅 : 부모 객체에서 자식 객체로 형변환하는 것을 의미한다.
- 자식 타입의 객체를 참조하던 부모 타입 변수를 자식 타입으로 형변환해주는 것은 가능하다.
- 다운 캐스팅은 반드시 명시적 형변환을 해주어야 한다.
- 부모 타입의 변수가 자식 타입으로 명시적 형변환을 할 땐 문제가 없지만, 같은 부모를 가진 다른 자식 타입으로 명시적 형변환을 할 때에는 런타임 오류가 발생한다.
- 자식 타입끼리 서로가 공유하지 않는 멤버들이 존재할 수도 있기 때문이다.
- is 키워드와 as 키워드를 이용하여 형변환할때 해당 객체를 참조하고 있는지 확인할 수 있다.
Character[] characters = new Character[5];
characters[0] = new Player("Player1", 5);
characters[1] = new Character("Character", 2);
characters[2] = new Enemy("Enemy", 7);
characters[3] = new Player("Player2", 4);
characters[4] = new Character("Character2", 9);


- A obb2 = (A) obb; 는 B공간안의 A공간에 접근하는것이다.
- obb2.b=20;은 성립하지 않는다. => A공간에 접근 한것이지 B의 공간을 사용하는것이 아니기에 b의 공간에 접근하지 못한다.
- B obb3 = (B) obb2;는 다운 캐스팅으로 A에서 A의 공간을 가지고 있는 B로 이동하는것이기에 가능한 것이다.
- obb2에서 a=10; , b=20;을 정의했고, obb3.a , obb3.b를 부른다면 이미 정의되어있는것에 10과 20이 들어가있고 접근이 가능하다.
11. 제네릭
- 클래스 또는 메서드가 클라이언트 코드에 의해 선언되고 인스턴스화될 때까지 하나 이상의 형식 지정을 연기하는 클래스 및 메서드를 디자인할 수 있도록한다.
- 즉, 제네릭 형식 매개 변수 T를 사용하여 자료형을 선택하지 않은채 작성한 다음, 선언할때 해당 자료형을 넣어 선언한다.
- 제네릭은 컬렉션(배열, 컨테이너) 및 해당 컬렉션에서 작동하는 메서드에서 가장 자주 사용된다.
- 제네릭 형식을 사용하여 코드 재사용, 형식 안전성 및 성능을 최대화한다.
- 가장 일반적으로 제네릭은 컬렉션 클래스를 만드는 데 사용된다.
- 사용자 고유의 제네릭 인터페이스, 클래스, 메서드, 이벤트 및 대리자를 만들 수 있다.
private void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int val1 = 10;
int val2 = 20;
Swap<int>(ref val1, ref val2);
print($"{val1}, {val2}");
string val3 = "abc";
string val4 = "def";
Swap<string>(ref val3, ref val4);
print($"{val3}. {val4}");


11-2 제네릭 스택 구현
class Stack<T>
{
private int top = -1;
private T[] values;
public const int MaxCount = 10; //const : 상수 => 절대 변하지 않는 수, 반드시 초기화 시켜야한다.
public Stack()
{
values = new T[MaxCount];
}
public void push(T value)
{
if(top +1 >= MaxCount)
{
throw new System.Exception("스택 꽉참");
}
values[++top] = value;
}
public T Pop()
{
if (Empty())
{
throw new System.Exception("스택 비어있음");
}
T value = values[top--];
return value;
}
public T Front()
{
return values[top];
}
public bool Empty()
{
return top < 0 ? true : false;
}
}
Stack<int> stack = new Stack<int>();
stack.push(9);
stack.push(20);
stack.push(10);
stack.push(50);
stack.push(4);
stack.push(3);
while (stack.Empty() == false)
{
print(stack.Pop());
}
for(int i = 0; i < 11; i++)
{
stack.push(i);
}


12. const
- 상수(const)는 컴파일 시간에 변경할 수 없는 값이다.
- 열거형 형식을 사용하여 정수 계열 기본 제공 형식(int, uint, long 등)에 대한 명명된 상수를 정의할 수 있다.
- 상수는 선언될 때 초기화되어야한다.
- 상수에 액세스할 때에는 클래스이름.상수명 으로 사용해야한다.
public const int MaxCount = 10; //const : 상수 => 절대 변하지 않는 수, 반드시 초기화 시켜야한다.
13. SerializeField
- private 접근 지정자는 선언된 클래스 또는 구조체의 본문 내에서만 액세스할 수 있지만, [SerializeField]를 앞에 붙인다면 인스펙터 창에서는 접근가능하게 만들어주는 명령어다.
- 직렬화 : 추상적인 데이터를 전송 가능하고 저장 가능한 형태로 바꾸는 것을 의미한다.
- 간단하게, 유니티가 private 필드를 직렬화(serialization)하도록 설정하는 것이다.
[SerializeField]
private GameObject _prefab;
