C#에서 생성자 내 가상 멤버 호출: 경고 및 해결 방법
C#에서 생성자 내 가상 멤버 호출: 경고 및 해결 방법
경고 발생 원인
가상 함수는 파생 클래스에서 재정의될 수 있는 함수입니다. 생성자 내에서 가상 함수를 호출하면 호출되는 함수는 실제 객체의 유형에 따라 다릅니다. 하지만 객체가 완전히 생성되기 전이라 실제 객체 유형을 확실하게 알 수 없기 때문에 문제가 발생합니다.
예를 들어 다음 코드를 살펴보세요.
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
}
}
이 코드에서 Base
클래스의 생성자는 DoSomething()
가상 함수를 호출합니다. 하지만 derived
객체가 생성될 때 호출되는 실제 함수는 Derived
클래스에서 재정의된 DoSomething()
함수입니다.
만약 Derived
클래스의 DoSomething()
함수가 Base
클래스의 DoSomething()
함수를 호출한다면 객체가 완전히 초기화되기 전에 Base
클래스의 멤버 변수에 접근하게 됩니다. 이는 예상치 못한 동작이나 오류로 이어질 수 있습니다.
경고 해결 방법
이러한 경고를 해결하려면 다음 방법을 사용할 수 있습니다.
- 생성자 내에서 가상 함수 호출을 피하십시오. 가능하다면 생성자 내에서 가상 함수 호출을 피하는 것이 가장 안전합니다. 대신, 가상 함수를 호출해야 하는 경우 별도의 메서드를 만들고 생성자 후에 호출하십시오.
base.DoSomething()
을 사용하십시오. 만약 반드시 생성자 내에서 가상 함수를 호출해야 하는 경우,base.DoSomething()
을 사용하여 항상 기본 클래스의 버전을 호출하십시오. 이렇게 하면 파생 클래스에서 재정의된 함수가 아닌 기본 클래스의 함수가 항상 호출됩니다.this
키워드를 사용하십시오. C# 8.0부터는this
키워드를 사용하여 생성자 내에서 현재 객체의 인스턴스 메서드를 호출할 수 있습니다. 이 방식을 사용하면 실제 객체 유형에 관계없이 항상 현재 객체의 메서드를 호출할 수 있습니다.
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
derived.DoSomething(); // this 키워드를 사용하여 현재 객체의 DoSomething() 함수 호출
}
}
결론
예제 코드: C#에서 생성자 내 가상 멤버 호출 및 해결 방법
경고 발생 코드
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
}
}
이 코드는 다음과 같은 경고를 발생시킵니다.
Base.cs(9, 13): 'DoSomething' hides inherited member 'Base.DoSomething()'.
This warning could lead to runtime problems because the new definition hides the base class definition.
해결 방법 1: 생성자 내 가상 함수 호출 피하기
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
public void Initialize()
{
DoSomething(); // 생성자 후에 DoSomething() 호출
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
derived.Initialize();
}
}
해결 방법 2: base.DoSomething() 사용
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
public Derived()
{
base.DoSomething(); // 항상 기본 클래스의 DoSomething() 호출
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
}
}
해결 방법 3: this 키워드 사용 (C# 8.0 이상)
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
public Derived()
{
this.DoSomething(); // 현재 객체의 DoSomething() 호출
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
}
}
C#에서 생성자 내 가상 멤버 호출 대체 방법
생성자 내에서 가상 함수 호출 피하기:
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
public void Initialize()
{
DoSomething(); // 생성자 후에 DoSomething() 호출
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
derived.Initialize();
}
}
base.DoSomething() 사용:
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
public Derived()
{
base.DoSomething(); // 항상 기본 클래스의 DoSomething() 호출
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
}
}
this 키워드 사용 (C# 8.0 이상):
class Base
{
public virtual void DoSomething()
{
Console.WriteLine("Base DoSomething");
}
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
public Derived()
{
this.DoSomething(); // 현재 객체의 DoSomething() 호출
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
}
}
추상 메서드 사용:
만약 파생 클래스에서 반드시 특정 동작을 구현해야 하는 경우, 추상 메서드를 사용하는 방법도 있습니다. 추상 메서드는 기본 클래스에서 선언되지만 구현은 파생 클래스에서 제공됩니다. 생성자에서 추상 메서드를 호출하면 파생 클래스에서 정의된 구현이 항상 사용됩니다.
class Base
{
public abstract void DoSomething();
}
class Derived : Base
{
public override void DoSomething()
{
Console.WriteLine("Derived DoSomething");
}
public Derived()
{
DoSomething(); // 파생 클래스의 DoSomething() 구현 호출
}
}
public class Program
{
public static void Main(string[] args)
{
var derived = new Derived();
}
}
주의 사항:
- 생성자 내에서 가상 함수를 호출할 경우에는 주의가 필요하며, 위에서 제시된 대체 방법을 사용하는 것이 좋습니다.
this
키워드를 사용하는 방법은 C# 8.0 이상에서만 사용 가능합니다.- 추상 메서드를 사용하는 경우, 파생 클래스에서
c# constructor warnings