본문 바로가기
책/Effective C#

Effective C# - Item 16 생성자 내에서는 절대로 가상 함수를 호출하지 말라

by 코딩하는 돼징 2023. 12. 3.
반응형

객체가 완전히 생성되기 이전에 가상 함수를 호출하면 이상 동작을 일으킨다. 어떤 타입이든 생성자가 수행을 완료할 때까지는 완전히 생성되었다고 할 수 없다. 따라서 생성자 내에서 가상 함수를 호출하면 예상 처럼 동작하지 않는다.

class B
{
    protected B()
    {
        VFunc();
    }
    
    protected virtual void VFunc()
    {
        Console.WriteLine("VFunc in B");
    }
}
class Derived : B
{
    private readonly string msg = "Set by initailizer";
    
    public Derived(string msg)
    {
        this.msg = msg;
        onsole.WriteLine("Program 출력 " + msg);
    }
    
    protected override void VFunc()
    {
        Console.WriteLine("Override 출력 " + msg);
    }
    
    public static void Main()
    {
       var d = new Derived("Construced in main");
    }
}

무엇이 출력될까 ?

과정을 살펴보자

1) Main 메서드에서 Derived 클래스의 객체 d를 생성한다.
2) Derived 클래스의 생성자는 먼저 B 클래스의 생성자를 호출한다. 이때 B 클래스의 생성자 내에서 VFunc() 메서드가 호출된다. 아직 초기화 되지 않았으므로 "Override 출력 Set by initializer"가 출력된다.

3) 다음으로 Derived 클래스의 생성자가 실행된다. 이때, msg 변수가 "Constructed in main"으로 초기화되고 "Program 출력 Constructed in main"이 출력된다.

생성자 체인(Constructor chain)

상속 계층 구조를 따라서 상위 클래스의 생성자가 먼저 호출되고 그 후에 파생 클래스의 생성자가 호출된다. 이런 순서로 상위 클래스에서 하위 클래스로 생성자 체인이 호출된다고 한다.


결론

베이스 클래스의 생성자 내에서 가상 함수를 호출하면 파생 클래스가 가상 함수를 어떻게 구현했는지에 따라 매우 민감하게 동작하게 된다. 파생 클래스가 어떻게 작성될지 예상할 수는 없는 노릇이므로 베이스 클래스의 생성자 내에서 가상함수를 호출하게 되면 구조가 매우 취약한 코드가 되어버린다. 

 

 

 

 

 

본 게시글은 Effective C#을 읽고 정리하였습니다.

 

 

 

반응형

댓글