본문 바로가기

개발/Unity, C#

C# 생성자와 가상 함수

기본적인 C# 생성자 호출 순서

using System;

class Base
{
    public Base() 
    { 
        Console.WriteLine("Base");
    } 
}
class Derived : Base
{
    public Derived()
    {        
        Console.WriteLine("Derived");
    }
}

class Program
{
    public static void Main()
    {
        Derived d = new Derived();
    }
}

기반 클래스 생성자 -> 파생 클래스 생성자 순으로 호출되고

 

컴파일러가 파생 클래스 생성자 public Derived()에서 public Derived() : base()로 만들어줌

 

 

생성자에서 가상 함수를 호출하는 경우

using System;

class Base
{
    public Base() 
    { 
        Foo(); 
    }

    public virtual void Foo() 
    {
        Console.WriteLine("Base.Foo");
    }    
}
class Derived : Base
{
    public int a = 1;
    public int b;
    
    public Derived()
    {
        b = 1;
    }
    public override void Foo() 
    {
        Console.WriteLine($"Derived.Foo : {a}, {b}"); 
    }
}

class Program
{
    public static void Main()
    {
        Derived d = new Derived();
    }
}

결과로 1, 0이 나오는데 그 이유는 컴파일할 때

public Derived()
{
    a = 1;
    base();
    b = 1;
}

이렇게 만들어짐

 

결론은 생성자에서 가상 함수 호출을 하지 말자.

 

추가

오버라이드 된 함수의 선택적 매개변수

using System;

class Base
{
    public virtual void Foo(int a = 1)
    {
        Console.WriteLine($"Base.Foo({a})");
    }
}

class Derived : Base
{
    public override void Foo(int a = 2)
    {
        Console.WriteLine($"Derived.Foo({a})");
    }
}

class Program
{
    public static void Main()
    {
        Base b = new Derived();
        b.Foo();
    }
}

파생 클래스의 객체의 Foo가 호출됐지만 기반 클래스의 매개변수가 사용됨

 

이렇게 된 이유는

 

선택적 매개변수는 컴파일 단계에서 결정되고 가상 함수의 호출은 런타임에 결정됨

 

그래서 컴파일 단계에서 b.Foo(10)으로 만들어지고

 

b는 런타임에 결정돼 파생 클래스가 불려짐

 

결론은 가상 함수에 선택적 매개변수를 쓰면 버그가 날 확률이 높다.