virtual method의 정의, 구현방법, 사용, 장단점에 대해 살펴보자.
Virtual method란?
object-oriented programming의 기존 개념이다. derived class에서 동일한 signature의 method로 override되는 것을 허용한다.
overriding은 선택사항이며, derived class는 base class의 기능을 그대로 사용할 수 있다.
Method는 기본으로 non-virtual하며, override될 수 없다. method의 return type앞에 virtual 키워드를 추가하므로 virtual method로 선언할 수 있다. virtual은 return type을 갖을 수도 없을 수도 있다.
public virtual double CalculateArea()
virtual keyword는 다음의 함수에서 사용할 수 없다.
- static
- abstract
- constructor 또는 accessor(get, set)
- base class로부터 이미 override된 virtual method
- sealed (sealed class)
- private
Late Binding
C#은 두가지 타입의 bindings을 구현한다.
- early binding : static binding, compile-time binding, compile time에 method를 구현한다.
- late binding : dynamic binding, run-time polymorphism. run-time에 method의 실제 구현을 결정한다. virtual methods를 사용하여 late binding을 얻을 수 있다.
Dynamic Dispatch
class에 virtual method가 있는 경우, 컴파일러는 자동으로 class의 virtual method table을 생성한다. v-table은 class의 virtual method pointers 리스트를 갖는다. instance에서 virtual method를 호출할 때마다 pointers를 사용한다.
compile time동안에는 base 또는 derived class의 함수를 호출할지 모르기 때문에, pointer는 applicable method 확인한다.
이 과정을 dynamic dispatch라 한다. run-time시 선언된 object type이 아닌, 실제 type을 기준으로 virtual method의 정확한 구현을 호출하는 책임이 있다.
Virtual Methods 구현
base class 'Shape'에 2 virtual 함수와 1 non-virtual 함수를 추가하자.
public class Shape
{
public virtual double CalculateArea()
{
return 0;
}
public virtual string GetShapeType()
{
return "This is a generic shape";
}
public string Draw()
{
return "Drawing a generic shape";
}
}
이제 base class와 interact할 2개의 derived classes를 정의하자.
하나는 'Rectangle'
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double CalculateArea()
{
return Width * Height;
}
public override string GetShapeType()
{
return "This is a rectangle";
}
public new string Draw()
{
return "Drawing a rectangle";
}
}
하나는 'Circle'
public class Circle : Shape
{
public double Radius { get; set; }
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
public new string Draw()
{
return "Drawing a circle";
}
}
new keyword는 compiler에서 base class의 method는 숨길 것을 지시한다.
var shape = new Shape();
var circle = new Circle() { Radius = 2 };
var rectangle = new Rectangle() { Height = 2, Width = 3 };
Console.WriteLine($"Shape area: {shape.CalculateArea()}"); // Shape area: 0
Console.WriteLine($"Circle area: {circle.CalculateArea()}"); // Circle area: 12,566370614359172
Console.WriteLine($"Rectangle area: {rectangle.CalculateArea()}"); // Rectangle area: 6
virtual method를 override하는 것이 필수는 아니다. derived class에서 override하지 않으면 base class의 함수가 실행된다.
Console.WriteLine($"Shape type: {shape.GetShapeType()}"); // Shape type: This is a generic shape
Console.WriteLine($"Circle type: {circle.GetShapeType()}"); // Circle type: This is a generic shape
Console.WriteLine($"Rectangle type: {rectangle.GetShapeType()}"); // Rectangle type: This is a rectangle
Draw() 함수를 호출해보자. Circle class를 Shape으로 선언했다.
Shape circleAsShape = new Circle() { Radius = 2 };
Console.WriteLine($"Shape draw: {shape.Draw()}"); // Shape draw: Drawing a generic shape
Console.WriteLine($"Circle draw: {circleAsShape.Draw()}"); // Circle draw: Drawing a generic shape
Console.WriteLine($"Rectangle draw: {rectangle.Draw()}"); // Rectangle draw: Drawing a rectangle
사용, 장단점
frameworks, class libraries, testing 개발 시 구현한다. base class의 동작변경 없이, classes의 확장과 커스텀을 가능하게 한다. 또한 base classes의 mock objects를 만들어 테스트할 수 있게 한다.
Polymorphism은 코드 재사용을 할 수 있게 하고, base class의 동작과 동일하다면 재구현할 필요가 없다. base class에 typical 구현을 추가하므로, class의 general 기능을 정의할 수 있다.
이러한 장점에도 불구하고, 조심스럽게 사용해야 한다. 여러 단계의 overriding은 코드 가독성을 떨어뜨린다.
Late binding은 performance에 영향을 줄 수 있다.
performance-critical 코드나 tight loops에서는 virtual method 사용을 피한다. 그 대신 interfaces나 delegates를 고려할 수 있다.
결론
virtual method는 OOP의 기본 특성이며, polymorphism, late binding, dynamic method dispatch, 유연성, 확장성을 제공한다.
적절히 사용하면, 깔끔하고 유지보수하기 수월한 코드를 만들 수 있지만, 퍼포먼스 영향도 고려해야 한다.
'C#' 카테고리의 다른 글
C#] SOLID 원칙 1. Single Responsibility Principle (SRP 원칙) (2) | 2024.01.26 |
---|---|
C#] SOLID 원칙 2. Open Closed Principle (OCP 원칙) (1) | 2024.01.23 |
C#] const vs readonly 차이점 (1) | 2024.01.19 |
C#] File - 사용 중인 파일 확인 방법 (IOException, HResult) (0) | 2024.01.18 |
C#] 식별자/변수명 네이밍 규칙, convention (0) | 2024.01.12 |
댓글