본문 바로가기
C#

C#] Virtual Method

by Fastlane 2024. 1. 23.
728x90
반응형

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, 유연성, 확장성을 제공한다. 

 

적절히 사용하면, 깔끔하고 유지보수하기 수월한 코드를 만들 수 있지만, 퍼포먼스 영향도 고려해야 한다. 

 

 

728x90
반응형

댓글