본문 바로가기

[내배캠] 본 캠프 개발 학습/자세히 살펴보는 Q&A

C# static 메서드 내의 클래스 필드 참조

  • 문제

인벤토리에서 장착한 아이템의 속성값을 플레이어 정보창 내 속성값에 (+x) 으로 출력하려고 하려고 한다. 

공격력: 10 (+7)
방어력: 10 (+5)


구현하려고 했던 방식은 다음과 같다. 

 public class Inventory
 {
  public int attackIncrease = 0;
  public int defenseIncrease = 0;
  
 // 필드에 공격력/방어력 증가값 변수를 0으로 최초 선언
 
 public static void ManageEquipment()
{
  // 새 아이템 장착
 Player.attackPower += selectedItem.attackPower;
 Player.defense += selectedItem.defense;

 Inventory inventory = new Inventory();
 inventory.attackIncrease = selectedItem.attackPower; 
 inventory.defenseIncrease = selectedItem.defense;
 // Inventory를 동적 변수로 생성

 selectedItem.isEquipped = true;
 equippedItem = selectedItem;
 Console.WriteLine($"{selectedItem.name}을(를) 장착했습니다.");
 ManageEquipment();
 
 }


인벤토리 클래스에서 Inventory 동적 변수를 생성해 장착한 아이템의 공격력과 방어력을 필드에서 선언한 변수에 저장하고, 

    public class Player
    {
    
    public static void ShowStatus(Inventory inventory)
    {

    Console.WriteLine("\n===== 상태창 =====");
    Console.WriteLine($"이름: {playerName}");

    Console.WriteLine($"레벨: {playerLv}");
    Console.WriteLine($"직업: {playerJob}");

    int attackDifference = Inventory.attackIncrease;
    int defenseDifference = Inventory.defenseIncrease;

    if (attackDifference > 0)
    {
        Console.WriteLine($"공격력: {Player.attackPower} (+{attackDifference})");
    }
    else
    {
        Console.WriteLine($"공격력: {Player.attackPower}");
    }

    if (defenseDifference > 0)
    {
        Console.WriteLine($"방어력: {Player.defense} (+{defenseDifference})");
    }
    else
    {
        Console.WriteLine($"방어력: {Player.defense}");
    }

    Console.WriteLine($"체력: {hp}");
    Console.WriteLine($"Gold: {playergold}G");

    Console.WriteLine("===================\n");

 

플레이어 클래스에서 인벤토리 클래스의 Inventory 동적 변수를 받아와 공격력/방어력에 출력한다.

  • 변수(new 인스턴스 생성) 의도 

다른 클래스의 필드나 변수는 단순히 public 으로 선언한다고 해서 사용할 수 없다. 

public 필드는 상점에서 파는 물건이다.
'팔겠다'고 선언만 해둔 것이지 상점이 없으면 그 물건은 사용할 수 없다. 
이때, new 키워드로 접근하면 상점을 만들 수 있게 된다.

 

  • Static이 아닌 필드, 메서드 또는 속성 " "에 개체 참조가 필요합니다.
int attackDifference = Inventory.attackIncrease;
int defenseDifference = Inventory.defenseIncrease;

 

하지만 이렇게 진행했을 때 위의 두 변수에서 개체 참조가 필요하다는 오류가 발생한다. 

  • 원인

클래스와 변수에 대한 개념이 부족해 코드에 문제가 많지만,
Static 메소드 안에서 Static이 아닌 변수를 사용한 것이 원인이었다.


Q. 다른 클래스의 필드나 변수를 가져오기 위해서 인스턴스를 생성해야 하는 게 아니었나?


이를 이해하기 위해선 스테틱(Static)에 대한 개념 정립이 필요하다.

 

각 스크립트는 독립적으로 존재한다. 
하지만 스테틱(Static) 키워드를 사용하면 해당 필드, 메서드, 속성 등이 프로그램 실행 시점부터 메모리에 저장되고, 별도의 인스턴트를 만들지 않고도 접근할 수 있다. 


프로그램 실행 시점부터 상점이 없어도 물건을 팔 수 있게 되는 것이다. 

 

  • 변수(인스턴스 변수)는 static 메서드 내에서 직접 사용할 수 없다.

Q. 그래서 static 메서드에 왜 동적 변수를 넣을 수 없는 걸까?

 

static은 상점이 없어도 물건을 팔 수 있는 대신 이 물건을 계속 가지고 있어야 된다는 단점을 가지고 있다.
정해진 물건들로만 거래해야 한다는 의미이다. 

내가 썼던 상태창 오류 코드로 자세히 살펴보자.

    public class Player
    {
    
    public static void ShowStatus(Inventory inventory)
    {

    Console.WriteLine("\n===== 상태창 =====");
    Console.WriteLine($"이름: {playerName}");

    Console.WriteLine($"레벨: {playerLv}");
    Console.WriteLine($"직업: {playerJob}");

    int attackDifference = Inventory.attackIncrease;
    int defenseDifference = Inventory.defenseIncrease;

    if (attackDifference > 0)
    {
        Console.WriteLine($"공격력: {Player.attackPower} (+{attackDifference})");
    }
    else
    {
        Console.WriteLine($"공격력: {Player.attackPower}");
    }

    if (defenseDifference > 0)
    {
        Console.WriteLine($"방어력: {Player.defense} (+{defenseDifference})");
    }
    else
    {
        Console.WriteLine($"방어력: {Player.defense}");
    }

    Console.WriteLine($"체력: {hp}");
    Console.WriteLine($"Gold: {playergold}G");

    Console.WriteLine("===================\n");


'상태창'이라는 공간은 상점 없이도 '항상 정해진 물건들(이름, 레벨, 직업, 체력, 골드)'을 팔 수 있다.
각 물건(객체)을 프로그램 종료까지 가져가야 한다.

하지만 여기서 변수(공격력 증가, 방어력 증가)라는 새로운 물건이 추가되면서 규칙을 깨버렸기 때문에,
'상태창' 상점에(static 메서드) 혼란이 발생하게 되는 것이다. 

 

  • 해결
 public static int attackIncrease = 0;
 public static int defenseIncrease = 0;


 static 변수로 변경해 모든 클래스에서 접근할 수 있도록 한 다음, 

      if (Inventory.attackIncrease > 0)
      {
          Console.WriteLine($"공격력: {Player.attackPower} (+{Inventory.attackIncrease})");
      }
      else
      {
          Console.WriteLine($"공격력: {Player.attackPower}");
      }

      if (Inventory.defenseIncrease > 0)
      {
          Console.WriteLine($"방어력: {Player.defense} (+{Inventory.defenseIncrease})");
      }
      else
      {
          Console.WriteLine($"방어력: {Player.defense}");
      }

 

상태창에 해당 static 변수를 불러와 사용했다.

해결은 쉬웠지만 좋은 코드는 아니다. 
정적 변수는 프로그램 종료까지 가지고 있어야 해서 규칙을 깨기가 어렵고, 메소드가 무거워진다. 

정적이 아닌 동적 메소드로 바꾸는 게 더 좋은 해결 방안이었을 것이라고 생각된다.