오늘은 제네릭과 오버라이딩, 추상클래스를 실습. 기존 코드를 리팩토링 해보는 과정
심화 과제 1
- 바로 위에서, Trainer라는 클래스를 만들었습니다.
- 해당 구현 과제를 추상 클래스를 활용하여 리팩토링(다시 만들기)를 진행하려 합니다. 다음 구현사항에 맞추어 Trainer클래스를 완성시켜 주시기 바랍니다.
- Monster라는 추상 클래스를 새로 하나 만들고, 해당 클래스의 필드로는 정수형 레벨, 열거형 몹타입, 그리고 문자열 이름을 넣어서 만들어 주시기 바랍니다.
- Monster 추상 클래스에 BaseAttack이라는 abstract 메소드도 추가하여 주시기 바랍니다.
심화 과제 2
- 이 Monster 클래스를 상속받는 Pikachu, Squirtle, Bulbasaur, Charmander 네 가지의 클래스를 만들어 주시기 바랍니다.
- Pikachu의 BaseAttack에는 ‘전광석화’, Squirtle는 ‘물총발사’, Bulbasaur는 ‘덩굴채찍’, Charmander는 ‘화염방사’ 를 콘솔에 출력하는 기능을 각각 제작하여 주시기 바랍니다.
- 각각 몬스터들의 생성자에는 인자값으로 이름과 레벨을 받을 수 있게 하고, 몹타입은 본인 취향 적절한 타입을 기입하여 주시기 바랍니다.
심화 과제 3
- Trainer 클래스에 6개의 몬스터형을 담을 수 있는 배열이 있을겁니다.
- 트레이너 생성자에는 자동으로 피카츄를 배열의 첫 번째에 담기게 하는 코드를 작성하여 주시기 바랍니다.
심화 과제 4
- Trainer 클래스의 메소드로는, 몬스터를 인자값으로 받아 추가하는 기능,
- 만약 6마리를 초과해서 추가하려 하면, 추가하지 않고 경고문을 내는 메소드를 하나 제작해주시기 바랍니다.
- 이를 제작하기 위해 추가 필드가 필요하다면 추가 작성하시길 바랍니다.
심화 과제 5
- 배열 속 보유중인 모든 몹들이 가진 BaseAttack을 전부 실행하는 AllAttack이라는 메소드를 작성하세요.
심화 과제 6
- 메인에서 Trainer를 하나 만든 후, Trainer 객체에 Charmander 한마리를 이름은 ‘파이리’, 레벨은 5로 하여 추가하는 코드를 작성하세요.
- Trainer의 AllAttack 메소드를 호출하여 트레이너가 가진 모든 몹의 공격이 콘솔에 출력되게 하세요.
Main.cs
Console.WriteLine("와 오늘부터 포켓몬 트레이너다, 내 이름은? \t");
string myName = Console.ReadLine();
Trainer trainer = new Trainer(myName, 1, "피카츄");
Monster pyri = new Charmander(5, "파이리");
trainer.AddMonster(pyri);
Monster[] monsters = new Monster[5]
{
new Charmander(5, "파이리"),
new Squirtle(10, "꼬부기"),
new Bulbasaur(15, "이상해씨"),
new Charmander(20, "파이리"),
new Charmander(20, "파이리"),
};
// 꽉 찼습니다 프린트
foreach(Monster mons in monsters)
{
trainer.AddMonster(mons);
}
// 몬스터 상태 프린트
foreach(Monster mymons in trainer.Monsters)
{
Console.WriteLine("---");
mymons.PrintMonster();
Console.WriteLine("---");
}
trainer.AllAttack();
Monsters.cs
public abstract class Monster
{
protected int _level;
protected string _name;
protected MobType _type;
protected string _skill;
public Monster(int level, string name)
{
_level = level;
_name = name;
}
public abstract void BaseAttack();
public abstract void PrintMonster();
}
public class Pikachu : Monster
{
public Pikachu(int level, string name) : base(level, name)
{
_type = MobType.Electric;
_skill = "전광석화";
}
public override void BaseAttack()
{
Console.WriteLine($"{_skill}");
}
public override void PrintMonster()
{
Console.WriteLine($"유형 : {_type}");
Console.WriteLine($"레벨 : {_level}");
Console.WriteLine($"이름 : {_name}");
}
}
public class Squirtle : Monster
{
public Squirtle(int level, string name) : base(level, name)
{
_type = MobType.Water;
_skill = "물총발사";
}
public override void BaseAttack()
{
Console.WriteLine($"{_skill}");
}
public override void PrintMonster()
{
Console.WriteLine($"유형 : {_type}");
Console.WriteLine($"레벨 : {_level}");
Console.WriteLine($"이름 : {_name}");
}
}
public class Bulbasaur : Monster
{
public Bulbasaur(int level, string name) : base(level, name)
{
_type = MobType.Grass;
_skill = "덩굴채찍";
}
public override void BaseAttack()
{
Console.WriteLine($"{_skill}");
}
public override void PrintMonster()
{
Console.WriteLine($"유형 : {_type}");
Console.WriteLine($"레벨 : {_level}");
Console.WriteLine($"이름 : {_name}");
}
}
public class Charmander : Monster
{
public Charmander(int level, string name) : base(level, name)
{
_type = MobType.Fire;
_skill = "화염방사";
}
public override void BaseAttack()
{
Console.WriteLine($"{_skill}");
}
public override void PrintMonster()
{
Console.WriteLine($"유형 : {_type}");
Console.WriteLine($"레벨 : {_level}");
Console.WriteLine($"이름 : {_name}");
}
}
아래는 제네릭과 관련하여 추가적인 정리
C#은 모든것이 Object 로 이뤄져 있다.
- Object 마소 레퍼런스
- 자바의 Object 형과 비슷
제네릭의 장점?
- 역시 자바와 비슷.
- object 형식으로 다양한 형식을 다루면 성능 저하를 유발할 수 있고 위험하다.
- 수많은 다양한 형을 다루기 위해서는 좀 더 안전한 제네릭을 사용한다.
- 제네릭은 들어오는 타입에 제약도 걸 수 있고, 안전성도 보장된다.
- 추가적인 제네릭의 장점?
- 박싱과 언박싱을 할 필요가 없어진다.
// using generic
List<int> list = new List<int>();
list.Add(1);
int a = list[0];
// using non-generic collection
ArrayList list2 = new ArrayList();
int num = 1;
// ArrayList 에 넣는 순간 아래 처럼 박싱이 일어남
//object obj = (object)num;
list2.Add(num);
string unboxNum = (int)list2[0];
collection 라이브러리 살짝 미리보기
List.cs
public void Add(T item)
{
_version++;
T[] array = _items;
int size = _size;
if ((uint)size < (uint)array.Length)
{
_size = size + 1;
array[size] = item;
}
else
{
AddWithResize(item);
}
}
ArrayList.cs
public virtual int Add(Object value)
{
if (_size == _items.Length) EnsureCapacity(_size + 1);
_items[_size] = value;
_version++;
return _size++;
}
List.cs 에서는 컴파일 타임에 T 에 int 를 넣어, 자료형을 정확히 만들어서 구현함.
ArrayList에서는 모든것이 Object로 넣고 빠지기 때문에, 무조건 박싱이 일어난다~
- 선생님께 추가로 물어본 질문..
- 그래서 C#은 ArrayList가 왜 남아있는가..? 모든 면에서 List가 좋아보이는데?
- 하위 호환성때문에, 예전 제네릭 도입되기 이전코드들에서 (System.Collections)는 ArrayList 를 사용했었으나, 그 이후는 쓸 이유가 없음.
- List 형을 무조건 사용하는걸로~