- 클래스나 메서드를 정의할 때 타입을 매개변수로 받아서 처리하는 방식
- 클래스 또는 함수가 코드에 의해 엄선되고 인스턴스화될 때까지 형식의 사양을 연기하는 방법
- 기능을 구현한 뒤 자료형을 사용 당시에 지정해서 사용.
박싱 : 값형식 -> 참조형식
언박싱 : 참조형식 -> 값형식
static void Main(string[] args)
{
int value = 1;
// 업캐스팅 : 암시적으로 가능
// 박싱 : 값형식 -> 참조형식
object obj = value;
// 다운캐스팅 : 명시적으로 해야함
// 언박싱 : 참조형식 -> 값형식
int result = (int)obj;
Console.WriteLine("{0}, {1}, {2}", value, obj, result);
if (obj is int) // obj가 int형이면 true
{
int result2 = (int)obj;
Console.WriteLine(result2);
}
int a = 10;
int b = 20;
Util.Swap(ref a, ref b);
Console.WriteLine("{0}, {1}", a, b);
float fLeft = 10.5f;
float fRight = 20.5f;
Util.Swap(ref a, ref b);
bool bLeft = true;
bool bRight = false;
Util.Swap<bool>(ref bLeft, ref bRight); // <bool> : 자료형의 형식을 직접 지정도 가능
}
public class Util
{
//<일반화 함수>
//일반화는 자료형의 형식을 지정하지 않고 함수를 정의
// 기능을 구현한 뒤 사용 당시에 자료형의 형식을 지정해서 사용
public static void Swap<T>(ref T left, ref T right) // 함수명<T> : 자료형의 형식을 지정하지 않음
{
T temp = left;
left = right;
right = temp;
}
//public static T Add<T>(T left, T right) // X
//{ // bool 등 연산이 불가능한 자료형이 들어올수 있기때문에 쓸 수 없음
// return left + right; // 산술연산, 논리연산은 사용불가!
//}
}
- 일반화 자료형을 선언할 때 제약조건을 선언하여, 사용 당시 쓸 수 있는 자료형을 제한.
Item item1 = new Item();
item1.name = "포션";
Item item2 = new Item();
item2.name = "포션";
Console.WriteLine("두 아이템이 같은 아이템이다 결과 : {0}", EquaalItem(item1, item2));
int intBigger = Bigger(10,20);
double doubleBigger = Bigger(10.5, 20.5);
Item itemBigger = Bigger(new Item(), new Item()); // 아이템 정렬?
}
/// <summary>
/// 제약사항을 걸어서 Item 클래스를 상속받은 클래스만 사용가능
/// </summary>
/// <typeparam name="T">일반화 - 자료의 형식을 지정하지 않음</typeparam>
/// <param name="item">where T : Item -> Item 클래스를 상속받은 클래스만 사용가능</param>
public static bool EquaalItem<T>(T item1, T item2) where T : Item
{
return item1.name == item2.name;
}
public static T Bigger<T>(T left, T right) where T : IComparable<T> // 예시 - 이렇게도 쓸 수 있음
{ // T -> 결과도 T로 반환 -> 결과도 자료형에 맞는 걸로 받고 싶다.
if (left.CompareTo(right) > 0)
{
return left;
}
else
{
return right;
}
}
public class Item : IComparable<Item>
{
public string name;
public bool isUseble;
public int CompareTo(Item other)
{
return name.CompareTo(other.name);
}
}
public class Weapon : Item{ }
public class CusumableItem : Item { }
public class QuestItem : Item { }
- 인터페이스는 맴버를 가질 수 있지만 직접 구현하지 않음.단지 정의만을 가짐.
- 인터페이스를 가지는 클래스에서 반드시 인터페이스의 정의를 구현해야함.
- 이를 반대로 표현하자면 인터페이스를 포함하는 클레스는
반드시 인터페이스의 구성요소들을 구현했다는 것을 보장함.- Can-a 관계 : 클래스가 해당 행동을 할 수 있는 경우 적합함.
- 일반적으로 인터페이스의 이름은 I로 시작함. - 국룰!
- 인터페이스의 함수는 직접 구현하지 않고 정의만 진행.
public interface IEnterable // 인터페이스 선언
{
public void Enter(); // 구현하지 않고 정의만 함
}
public interface IOpenable // 인터페이스 선언
{
public void Open(); // 구현하지 않고 정의만 함
}
public class Enterance
{
// 로딩 기능 추가
// 플레이어 위치 이동시키기
public void Join() { }
}
public class LockPick
{
// 열쇠 기능
// 열려있는지 여부 확인기능
// 열렸을때 이펙트
public void Open() { }
}
public class Door : IEnterable, IOpenable // 인터페이스는 다중 상속 가능하다.
{
public void Enter() // 인터페이스는 구현이 강제 된다.
{
// 문을 열때 이펙트
Console.WriteLine("문을 열고 들어갔습니다.");
}
public void Open() // 인터페이스는 구현이 강제 된다.
{
// 문을 열때 이펙트
Console.WriteLine("문이 열렸습니다.");
}
}
public class Dungeon : IEnterable
{
public void Enter() // 인터페이스 상속시 구현이 강제 된다.
{
// 던전 입장시 이펙트
Console.WriteLine("던전에 입장했습니다.");
}
}
public class Chest : IOpenable
{
public void Open() // 인터페이스 상속시 구현이 강제 된다.
{
// 보물상자 열릴때 이펙트
Console.WriteLine("보물상자가 열렸습니다.");
}
}
public class Player
{
public void Enter(IEnterable enterable) // 들어갈수 있으면 들어갈게.
{
enterable.Enter();
}
public void Open(IOpenable openable) // 열수 있으면 열게.
{
openable.Open();
}
}
static void Main(string[] args)
{
Player player = new Player();
Dungeon dungeon = new Dungeon();
Chest chest = new Chest();
player.Enter(dungeon); // 던전에 입장했습니다
player.Open(chest); // 보물상자가 열렸습니다.
Door door = new Door(); // Door 클래스는 IEnterable, IOpenable 인터페이스를 모두 구현하고 있음.
player.Enter(door); // 문을 열고 들어갔습니다.
player.Open(door); // 문이 열렸습니다.
}