오늘 배운 내용들은 - 클래스, 객체, 프로퍼티, 아래는 심화문제

별도의 cs를 새로 만들고 Item이라는 클래스를 따로 제작.

  • 필드형으로는 ???형 아이템 이름
  • ??형 아이템타입
  • ??? 형 가격

Item.cs

// 심화과제 Item
public enum ItemType
{
    SWORD, ARMOUR, RING, NONE
}

public class Item
{
    private ItemType _itemType;
    private long _price;

    public string ItemName
    {
        get;
        set;
    }

    public ItemType Type
    {
        get
        {
            return _itemType;
        }
        set
        {
            _itemType = value;
        }
    }

    // 세터 선언할려면 둘다 해줘야됨 why?
    public long Price
    {
        // get;
        get
        {
            return _price;
        }
        set
        {
            if (value <= 0)
            {
                Console.WriteLine("값은 음수가 안됩니다");
                _price = 0;
            }
            else
            {
                _price = value;
            }
        }
    }

    public void PrintInfo()
    {
        Console.WriteLine("아이템 이름 : " + ItemName);
        Console.WriteLine("아이템 가격 : " + _price);   
        Console.WriteLine("아이템 유형 : " + _itemType);
    }
}

이어서 Inventory라는 클래스를 새로운 cs 시트에 제작,

  • 필드로 아이템 배열을 가지게 함
  • 추가적인 필드가 필요하면 생성
  • 이 클래스 내에 네가지 메소드 제작.
  • 첫번째 메서드명은 CreateInvenBySize에 인자값은 정수형 하나. 넘어온 인자값에 따라 아이템 배열을 할당해주고, 인자값만큼 인벤토리가 만들어졌다고 출력하는 기능 제작.
  • 두번째 메서드로는 반환값으로는 bool형, 함수명은 isInvenCreated, 함수 내용은 아이템배열이 0보다 작거나 null일 경우 false, 존재한다면 true반환
  • 세번째 함수는 GetItemByIndex, 리턴값은 아이템 함수 구현사항으로는 아이템 배열 중, 해당 인자값 위치의 아이템 반환하거나 비어있을 경우, null을 반환함과 동시에 비어있음 출력, 인자값은 정수형 인덱스,
  • 마지막 함수는 SetItemByIndex, 반환형은 없고, 인자값은 인덱스와 Item형 하나를 받는 함수 제작

Inventory.cs

public class Inventory
{
    private Item[] _items;
    
    public void CreateInvenBySize(int size)
    {
        // size가 0보다 같거나 작을경우, 0 귀찮아서 그냥 넘겨버려
        if (size <= 0)
        {
            Console.WriteLine("인벤토리가 0보다 같거나 작습니다, 다시 만드세요");
        }
        else
        {
            _items = new Item[size];
            Console.WriteLine($"{size}만큼 인벤토리가 만들어졌습니다.");                
        }
    }

    // 초기화 안되었거나, 초기화 사이즈가 0 보다 같거나 클경우 true
    public bool IsInvenCreated()
    {
        if (_items == null || _items.Length <= 0)
        {
            return false;
        }
        
        return true;
    }

    public Item GetItemByIndex(int inx)
    {
        // 초기화 안되었을때.
        if (IsInvenCreated() == false)
        {
            Console.WriteLine("비어있음");
            return null;
        }
        
        // 인덱스 값이 사이즈 보다 같거나 클때
        if (inx >= _items.Length)
        {
            Console.WriteLine("비어있음");
            return null;                
        }

        return _items[inx];
    }
    
    public void SetItemByIndex(int inx, Item item)
    {
        _items[inx] = item;
    }
}

Player라는 클래스를 새로운 cs에 만듭니다

  • 필드로 문자열형 이름, 정수형 HP, 정수형 공격력, 구조체 좌표(short형 x와 short형 y 두가지를 보유한 구조체), Inventory클래스로 만든 객체 하나를 보유하게 만듦.
  • MakeInven의 이름으로 인벤토리를 뉴할당시키는 함수 하나 제작, 프로퍼티를 통해 인벤토리를 get하는 기능 제작

Player.cs

public class Player
{
    struct Vector2
    {
        public short x;
        public short y;
    }
    private string _name;
    private int _hp;
    private int _attack;
    private Vector2 _pos;
    private Inventory _inv;

    public string PlayerName
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
        }
    }
    public Inventory MyInven
    {
        get
        {
            return _inv;
        }
    }

    public int Hp
    {
        get
        {
            return _hp;
        }
        set
        {
            if (value < 0)
            {
                Console.WriteLine("음수로 세팅 할 수 없습니다");
                _hp = 0;
            }
            else
            {
                _hp = value;
            }                
        }
    }

    public int Attack
    {
        get
        {
            return _attack;
        }
        set
        {
            _attack = value;
        }
    }

    public void MakeInven()
    {
        if (_inv == null || _inv.IsInvenCreated() == false)
        {
            _inv = new Inventory();
        }
    }

    // 0~9 으로 들어온다고 가정
    public void PrintMyInven(int inx)
    {
        if (_inv.IsInvenCreated() == false)
        {
            Console.WriteLine("내 인벤토리가 비어있습니다");
        }

        Item indexedItem = _inv.GetItemByIndex(inx);
        Console.WriteLine("인벤토리 위치 : " + inx);

        if (indexedItem == null)
        {
            Console.WriteLine("조회 불가능 인벤토리");
        }
        else
        {
            indexedItem.PrintInfo();
        }
    }
    
    public void AttackMonster(Monster monster)
    {
        monster.Hp -= _attack;
        
        if (monster.Hp <= 0)
        {
            Console.WriteLine($"{monster.Name}의 HP는 이미 0이야!!! ㅠㅠ");
            monster.Hp = 0;
            return;
        }
        
        Console.WriteLine($"{this._attack}만큼의 데미지를 {monster.Name}에 주었습니다.");            
    }        
}
  • main에서 플레이어 객체를 하나 만든 후, 플레이어의 함수를 통해 인벤토리를 활성화 하자. 다음엔 플레이어 속의 프로퍼티 인벤을 통해 인벤토리 클래스 속의 CreateInvenBySize함수를 통해 원하는 만큼의 아이템배열을 만든 후, 본인이 원하는 특정 위치에 아이템을 하나 만들어 넣도록 하자. 플레이어 속 혹은 인벤토리 속 다양한 함수를 직접 확인해 볼 수 있는 코드들을 메인에 적도록 하자.

  • 위에서 만든 Player 클래스 속의 hp와 데미지를 프로퍼티로 바꾸자. 메인을 싹 비우고, 플레이어 객체 하나와 몬스터 객체를 하나 만든 후, 몬스터에는 체력 100, 공격력 10을 넣어주자. 플레이어는 체력200, 공격력 20을 주어준 후, 각각의 HP와 공격을 get과 set을 활용하여 플레이어가 본인의 공격력을 활용해 몹을 한대 때려서 체력을 깎는 것을 출력하여보자. 객체를 만든 후 모든 공격과 체력이 닳는 과정은 메인서 콘솔로 출력해도 좋다

Program.cs

// 심화 과제 메인
public static void Main(string[] args)
{
    Player player = new Player();
    player.MakeInven();
    // 인벤 초기화 안하고 출력해보기
    player.PrintMyInven(5);
    player.MyInven.CreateInvenBySize(10);

    Item excalibur = new Item();
    excalibur.ItemName = "약속된 승리의 검";
    excalibur.Price = 1000000000000000000;
    excalibur.Type = ItemType.SWORD;

    player.MyInven.SetItemByIndex(4, excalibur);
    // 인덱스 넘었을때 출력해보기
    player.PrintMyInven(10);
    Console.WriteLine("");
    player.PrintMyInven(4);
    Console.WriteLine("");

    player.Hp = 200;
    player.Attack = 20;
    player.PlayerName = "나";

    Monster newMonster = new Monster();
    newMonster.Name = "슬라임";
    newMonster.Hp = 100;
    newMonster.Attack = 10;

    while (newMonster.Hp > 0 && player.Hp > 0)
    {
        player.AttackMonster(newMonster);
        monster.AttackPlayer(player);
        Console.WriteLine("=======");
    }
}

Monster.cs

// Monster 메서드는 사람이랑 똑같음 ㅎ 주는 인자만 다를 뿐
public void AttackPlayer(Player player)
{
    player.Hp -= _attack;
    
    if (player.Hp <= 0)
    {
        Console.WriteLine($"{player.PlayerName}의 HP는 이미 0이야!!! ㅠㅠ");
        player.Hp = 0;
        return;
    }
    
    Console.WriteLine($"{this._attack}만큼의 데미지를 {player.PlayerName}에 주었습니다.");            
}

더 구현해본것

  • AttackMonster와 AttackPlayer를 함수로 만들어 자동으로 싸우게한다
    • 이런게 오토 배틀러인가..?!?
  • PrintMyInven() 으로 인벤토리내 아이템을 출력한다.
    • 널포인터 방지를 위해 예외처리를 추가한다
  • Item.PrintInfo()로 아이템의 상태를 출력한다 -> 나중에 재사용 할수있도록

  • 오늘 배운 프로퍼티에 대해서 궁금한점을 남겨본다.
    • https://stackoverflow.com/questions/23102639/are-c-sharp-properties-actually-methods
    • 프로퍼티 쓰는법이 자바의 롬복과 매우 유사하다.
    • 요약하자면 컴파일러에서 컴파일시에 getter와 setter를 넣어준다는것.
    • https://csharpindepth.com/Articles/PropertiesMatter
    • 근데 위 답변에 링크달린 예제에 아주 재미있는 문제가..?!

재미있는 예제

MutableStruct.cs

struct MutableStruct
{
    public int Value { get; set; }

    public void SetValue(int newValue)
    {
        Value = newValue;
    }
}

class MutableStructHolder
{
    public MutableStruct Property { get; set; }
    public MutableStruct Field;
}

Main.cs

// 물어볼 내용
MutableStructHolder holder = new MutableStructHolder();
// Affects the value of holder.Field
holder.Field.SetValue(10);
// Retrieves holder.Property as a copy and changes the copy
holder.Property.SetValue(10);
// 왜 달라짐???
Console.WriteLine(holder.Field.Value);
Console.WriteLine(holder.Property.Value);
// holder.Property.Value = 0 으로됨.
  1. 일단 MutableStruct에 저런 이상한 코드를 넣으면 안됨. (자동으로 만드는 set; 과 SetValue를 둘다 설정..?)
  2. MutableStructHolder에 자동 생성자를 넣으면 이상해질수도 있다!?
    • 위의 holder.Property.Value에 setValue에 담기지 않는 이유는..?
    • 나름대로 생각한 이유를 그림으로 그려본다.. 아님 말구… 아님말구