안녕하세요!

정말 오랜만의 글입니다!


이 글에서는 C#의 base 키워드에 대해서 알아보려고 합니다.


base 키워드

해당 키워드를 사용하는 클래스의 부모 클래스를 가리키는 것

일단, 코드를 보시겠습니다.


class Fruit {
    string g_name;
    int g_grade;
    int g_price;
    
    public Fruit(string name, int grade, int price) {
        g_name = name;
        g_grade = grade;
        g_price = price;
    }
    
    public void Sell() {
        Console.WriteLine("과일 {0}을(를) {1}원에 팔았습니다."g_nameg_price);
    }
    
    public void Info() {
        Console.WriteLine("과일 이름: {0}"g_name);
        Console.WriteLine("과일 등급: {0}등급"g_grade);
        Console.WriteLine("과일 가격: {0}원"g_price);
    }
    
    public int Price {
        get { return g_price; }
    }
    public int Grade {
        get { return g_grade; }
    }
    public string Name {
        get { return g_name; }
    }
}

위 코드엔 Fruit 클래스가 선언되어 있습니다.

Sell, Info 메서드가 정의되어 있고, String, Grade, Price 속성이 정의되어 있습니다.

이제 base 키워드를 사용하는 예제를 보기 위해 Fruit 클래스를 상속하는 Apple 클래스를 선언해 보도록 하겠습니다.


class Apple : Fruit {
    public Apple(int grade, int price) : base("사과", grade, price) { }
    
    public void SellCustom() {
        Console.WriteLine("사과를 {0}원에 팔았습니다.", Price);
    }
    
    public void SellBase() {
        base.Sell();
    }
    
    public int Price {
        get { return 0; }
    }
}


Apple 클래스에서는 SellBase 및 SellCustom 메서드가 정의되었고, Fruit 클래스의 Price 속성과 같은 이름을 가진 속성이 정의되었습니다.

이 때 base 키워드는 아주 유용하게 사용될 수 있습니다.

base 키워드를 사용한 것과 사용하지 않은 것으로 Apple 클래스의 Price 를 접근하는지, Fruit 클래스의 Price 를 접근하는지가 달라지기 때문입니다.


그 이유는 base 키워드의 정의에 있습니다.

"해당 키워드를 사용하는 클래스의 부모 클래스를 가리키는 것"


그렇다면 base 를 사용한다면 Apple 클래스의 Price 가 아닌 Fruit 클래스의 Price 에 접근하게 되겠지요!

int priceBase = base.Price; // Fruit 클래스의 Price 속성 값
int priceThis = this.Price; // Apple 클래스의 Price 속성 값 (this. 는 생략 가능합니다)


그리고 생성자 부분을 보도록 하겠습니다.

public Apple(int grade, int price) : base("사과", grade, price) { }


생성자 본문이 비어있습니다.

{ } 이건 괄호를 한 줄에 배치한거예요~ 다른 뜻은 없습니다! ㅎㅎ


생성자 매개변수 뒷부분에 코드가 좀 이상한거 같죠?

base("사과", grade, price) { } "


이건 도대체 무슨 의미일까요??

답은 간단합니다. Apple 클래스를 생성할 때 Apple 의 부모 클래스인 Fruit 클래스의 생성자를 호출하여 값을 초기화한다는 의미입니다.


위의 예제에서는 생성자 본문이 비워져있지만, 생성자 본문을 비우지 않을 경우엔 base 클래스의 생성자를 호출한 후 해당 클래스의 생성자 본문을 실행하게 됩니다.


호출 순서는 이렇습니다.

1. Apple 클래스의 새 개체를 생성함 (Instance 를 만든다고 하죠)

2. Apple 클래스의 생성자를 호출.....하다가 : base 를 발견합니다.

3. Fruit 클래스의 생성자를 호출합니다.

4. Apple 클래스의 생성자 본문을 실행합니다.

5. Apple 클래스의 새 개체가 생성됩니다. (끝)



base 키워드의 특징

1. 정적(static) 클래스 또는 정적으로 선언된 속성, 메서드 또는 생성자에서는 base 키워드를 사용할 수 없다.

2. 여러 단계에 걸쳐 상속된 클래스인 경우, 상속된 모든 클래스의 필드, 속성, 메서드 등에 접근이 가능하다.

3. 생성자에서 base 키워드를 사용하여 부모 클래스의 생성자를 호출할 수 있다.

4. base.GetType() 메서드를 호출해도 부모 클래스의 형식을 반환하지는 않는다.

5. base 키워드는 "키워드" 다. 필드나 멤버 따위가 아니다!



1. 정적(static) 클래스 또는 정적으로 선언된 속성, 메서드 또는 생성자에서는 base 키워드를 사용할 수 없다.

정적 클래스의 경우엔 반드시 모든 멤버와 필드, 메서드, 속성 등이 모두 정적으로 선언되어야 하기 때문에 base 키워드를 사용할 수 없습니다.

(이 부분은 바로 뒤에 나올 정적으로 선언된 속성, 메서드 또는 생성자에서는 왜 base 키워드를 사용할 수 없는지를 설명하면서 같이 설명하겠습니다)


base 키워드를 보시면 파생 클래스에서 부모 클래스의 멤버, 필드, 메서드나 속성 등에 접근을 하려고 사용합니다.

그렇지만! static 으로 정의된 클래스는 개체 생성 없이 클래스.멤버 처럼 접근이 가능하기 때문에 base 키워드를 사용할 수 없습니다.

만약 정적 클래스에서 base 키워드를 사용할 수 있다면 다음과 같은 일이 발생하겠죠?? ^^


static class RootClass {
    public static string Name = "My Name!!!";
}
static class ICanUseBaseKeyword : RootClass {
    
    public static void BaseKeyword() {
         // base로 접근해야 하는지 RootClass로 접근해야 하는지....
        // 그것이 문제군요!
        Console.WriteLine(base.Name);
        Console.WriteLine(RootClass.Name);
    }
}

(당연히 이 코드는 실행이 되지 않습니다. 정적 클래스를 상속할 수 없을 뿐더러, 정적 클래스에서 base 키워드를 사용할 수도 없죠)



2. 여러 단계에 걸쳐 상속된 클래스인 경우, 상속한 모든 클래스의 필드, 속성, 메서드 등에 접근이 가능하다.

중첩된 상속 관계의 경우, 파생 클래스에서는 상속하는 모든 상위 클래스의 필드나 속성, 메서드 등에 접근이 가능합니다.

그렇다고 여러 클래스를 한번에 상속할 수 있다는 것은 아닙니다. (하지만 인터페이스의 경우엔 다중 구현이 가능합니다)


class Organic {
    public bool IsAlive;
    public bool IsHungry;
}

class Human : Organic {
    public int Height;
    public int Weight;
    public void UseTool(string tool) {
        Console.WriteLine("도구 {0} 을(를) 사용합니다.", tool);
    }
}

class Insect : Organic {
    public string Name;
}
class Ant : Insect {
    public void DischargingFormicAcid() {
        Console.WriteLine("개미산 배출!");
    }
    public void MarkPheromone() {
        Console.WriteLine("페로몬 표시!");
    }
    public void Bite() {
        Console.WriteLine("깨물기!");
    }
}
class BearAnt : Ant {
    public void BearAntInfo() {
        Console.WriteLine("곰개미");
    }
}
class HouseAnt : Ant {
    public void BearAntInfo() {
        Console.WriteLine("집개미");
    }
}

이 코드의 상속 관계는 이렇게 표현됩니다.


+ Organic

- Human

+ Insect

+ Ant

- BearAnt

- HouseAnt


Human 클래스에서는 자신의 클래스 멤버, 메서드 등과 Organic 클래스의 멤버, 메서드 등에 접근이 가능합니다.


 클래스

 접근 가능한 클래스

 Organic

 Organic

 Human

 Organic, Human
 Insect Organic, Insect

 Ant

 Organic, Insect, Ant

 BearAnt

 Organic, Insect, Ant, BearAnt

 HouseAnt

 Organic, Insect, Ant, HouseAnt

(이 표에서는 접근이 가능한 단계를 상세하게 나타내 주고 있습니다)



3. 생성자에서 base 키워드를 사용하여 부모 클래스의 생성자를 호출할 수 있다.

생성자에서는 base 키워드를 사용하여 부모 클래스의 생성자를 호출할 수 있습니다.

만약, 부모 생성자를 호출하지 못한다면 Human 클래스의 생성자 내용을 Boy 클래스의 생성자에 복붙해야겠죠?


class Human {
    public Human(string name) {
        Console.WriteLine("안녕하세요, 제 이름은 {0} 입니다.", name);
    }
}

class Boy : Human {
    public Boy(string name) : base(name) { }
}

(이 코드에서는 부모 클래스의 생성자를 호출하는 방법을 나타내 주고 있습니다)



4. base.GetType() 메서드를 호출해도 부모 클래스의 형식을 반환하지는 않는다.

base.GetType() 메서드를 호출해도 부모 클래스의 형식을 반환하지는 않습니다.

System.Object 클래스에 정의된 GetType() 메서드는 재정의가 불가능하기 때문에 정의된 대로만 사용됩니다.

그렇기 때문에 base.GetType() 메서드는 this.GetType() 메서드와 동일한 결과가 나오게 됩니다.


이유는 base.GetType() 메서드나 this.GetType() 메서드나 System.Object 클래스에 정의된 GetType() 메서드를 호출하기 때문입니다.

그리고, GetType() 메서드의 경우 클래스 내에서 사용되면 현재 클래스의 형식을 나타내고, 변수에서 사용되면 변수의 형식을 나타냅니다.

아래의 코드 실행 결과는 MoneyType 메서드는 Money, KRWType 메서드는 KRW, KRW를 출력합니다.


class Money {
    public void MoneyType() {
        Console.WriteLine(GetType());
    }
}

class KRW : Money {
    public void KRWType() {
        Console.WriteLine(GetType());
        Console.WriteLine(base.GetType());
    }
}

(이 코드에서는 GetType() 메서드와 base.GetType() 메서드를 호출하는 것을 나타내 주고 있습니다)



5. base 키워드는 "키워드" 다. 필드나 멤버 따위가 아니다!

base 키워드는 말 그대로 "키워드" 이기 때문에 변수를 통한 엑세스를 할 수가 없습니다. 엑세스를 할 수 없는게 아니라 시도 자체를 못합니다.

키워드란 ifforforeachprivatepublic 등과 같은 예약된 명령어를 지칭하는 단어입니다.


그렇기 때문에 이런 식으로는 사용이 불가능합니다.

Console.WriteLine("Apple 클래스의 부모 클래스인 Fruit 클래스의 Name 속성 값: {0}", apple.base.Name);



오랜만의 글이라 내용이 길어졌는데, 잘 이해가 될지 모르겠네요 ^^;

궁금한 점이 있으면 댓글 남겨주세요!! 메르스 조심하시구요~!!

+ Recent posts