다음을 통해 공유


abstract(C# 참조)

abstract 한정자는 수정되는 항목에 누락되거나 불완전한 구현이 있음을 나타냅니다. abstract 한정자는 클래스, 메서드, 속성, 인덱서 및 이벤트와 함께 사용될 수 있습니다. 클래스 선언에서 abstract 한정자를 사용하여 클래스가 자체에서 인스턴스화되지 않고 다른 클래스의 기본 클래스로만 사용됨을 나타냅니다. 추상으로 표시된 멤버는 추상 클래스에서 파생된 비 추상 클래스에 의해 구현되어야 합니다.

추상 클래스에는 추상 멤버(구현이 없고 파생 클래스에서 재정의되어야 합니다)와 완전히 구현된 멤버(예: 일반 메서드, 속성 및 생성자)가 모두 포함될 수 있습니다. 이렇게 하면 추상 클래스가 공통 기능을 제공하는 동시에 파생 클래스가 특정 추상 멤버를 구현하도록 요구할 수 있습니다.

예제 1 - 멤버가 혼합된 Abstract 클래스

다음 예제에서는 구현된 메서드와 추상 멤버를 모두 포함하는 추상 클래스를 보여 줍니다.

namespace LanguageKeywords;

public abstract class Vehicle
{
    protected string _brand;
    
    // Constructor - implemented method in abstract class
    public Vehicle(string brand) => _brand = brand;
    
    // Implemented method - provides functionality that all vehicles share
    public string GetInfo() => $"This is a {_brand} vehicle.";
    
    // Another implemented method
    public virtual void StartEngine() => Console.WriteLine($"{_brand} engine is starting...");
    
    // Abstract method - must be implemented by derived classes
    public abstract void Move();
    
    // Abstract property - must be implemented by derived classes  
    public abstract int MaxSpeed { get; }
}

public class Car : Vehicle
{
    public Car(string brand) : base(brand) { }
    
    // Implementation of abstract method
    public override void Move() => Console.WriteLine($"{_brand} car is driving on the road.");
    
    // Implementation of abstract property
    public override int MaxSpeed => 200;
}

public class Boat : Vehicle
{
    public Boat(string brand) : base(brand) { }
    
    // Implementation of abstract method
    public override void Move() => Console.WriteLine($"{_brand} boat is sailing on the water.");
    
    // Implementation of abstract property
    public override int MaxSpeed => 50;
}

public class AbstractExample
{
    public static void Examples()
    {
        // Cannot instantiate abstract class: Vehicle v = new Vehicle("Generic"); // Error!
        
        Car car = new Car("Toyota");
        Boat boat = new Boat("Yamaha");
        
        // Using implemented methods from abstract class
        Console.WriteLine(car.GetInfo());
        car.StartEngine();
        
        // Using abstract methods implemented in derived class
        car.Move();
        Console.WriteLine($"Max speed: {car.MaxSpeed} km/h");
        
        Console.WriteLine();
        
        Console.WriteLine(boat.GetInfo());
        boat.StartEngine();
        boat.Move();
        Console.WriteLine($"Max speed: {boat.MaxSpeed} km/h");
    }
}

class Program
{
    static void Main()
    {
        AbstractExample.Examples();
    }
}
/* Output:
This is a Toyota vehicle.
Toyota engine is starting...
Toyota car is driving on the road.
Max speed: 200 km/h

This is a Yamaha vehicle.
Yamaha engine is starting...
Yamaha boat is sailing on the water.
Max speed: 50 km/h
*/

이 예제에서 추상 클래스는 다음을 Vehicle 제공합니다.

  • 구현된 멤버: GetInfo() 메서드, StartEngine() 메서드 및 생성자 - 모든 차량에 공통 기능을 제공합니다.
  • 추상 멤버: Move() 메서드 및 MaxSpeed 속성 - 각 특정 차량 유형에 의해 구현되어야 합니다.

이 디자인을 통해 추상 클래스는 공유 기능을 제공하는 동시에 파생 클래스가 차량별 동작을 구현하도록 할 수 있습니다.

예제 2

이 예제에서 GetArea 클래스는 Shape에서 파생되므로 Square 구현을 제공해야 합니다.

abstract class Shape
{
    public abstract int GetArea();
}

class Square : Shape
{
    private int _side;

    public Square(int n) => _side = n;

    // GetArea method is required to avoid a compile-time error.
    public override int GetArea() => _side * _side;

    static void Main()
    {
        var sq = new Square(12);
        Console.WriteLine($"Area of the square = {sq.GetArea()}");
    }
}
// Output: Area of the square = 144

다음 기능이 있는 추상 클래스:

  • 추상 클래스는 인스턴스화할 수 없습니다.

  • 추상 클래스에 추상 메서드 및 접근자가 포함될 수 있습니다.

  • 추상 클래스에는 파생 클래스에 기능을 제공하는 구현된 메서드, 속성, 필드 및 기타 멤버도 포함될 수 있습니다.

  • sealed 한정자를 사용하여 추상 클래스를 수정할 수는 없습니다. 두 한정자가 상반된 의미가 있기 때문입니다. sealed 한정자를 사용하면 클래스가 상속되지 않고 abstract 한정자를 사용하려면 클래스가 상속되어야 합니다.

  • 추상 클래스에서 추상이 아닌 파생 클래스에는 모든 상속된 메서드 및 접근자의 실제 구현이 포함되어야 합니다.

메서드 또는 속성 선언에서 abstract 한정자를 사용하여 메서드 또는 속성에 구현이 포함되지 않음을 나타냅니다.

추상 메서드에는 다음과 같은 기능이 있습니다.

  • 추상 메서드는 암시적으로 가상 메서드입니다.

  • 추상 메서드 선언은 추상 클래스에서만 허용됩니다.

  • 추상 메서드 선언은 실제 구현을 제공하지 않으므로 메서드 본문이 없습니다. 메서드 선언은 세미콜론으로 끝나고 시그니처 뒤에 중괄호({ })가 없습니다. 예시:

    public abstract void MyMethod();  
    

    구현은 비추상 클래스의 멤버인 메서드 override에 의해 제공됩니다.

  • 추상 메서드 선언에서 static 또는 virtual 한정자를 사용하는 것은 오류입니다.

선언 및 호출 구문의 차이점을 제외하고 추상 속성은 추상 메서드처럼 동작합니다.

  • 정적 속성에서 abstract 한정자를 사용하는 것은 오류입니다.

  • override 한정자를 사용하는 속성 선언을 포함하여 상속된 추상 속성을 파생 클래스에서 재정의할 수 있습니다.

추상 클래스에 대한 자세한 내용은 추상 및 봉인 클래스와 클래스 멤버를 참조하세요.

추상 클래스는 모든 인터페이스 멤버에 대한 구현을 제공해야 합니다.

인터페이스를 구현하는 추상 클래스는 인터페이스 메서드를 추상 메서드에 매핑할 수 있습니다. 예시:

interface I
{
    void M();
}

abstract class C : I
{
    public abstract void M();
}

예제 3

이 예제에서 DerivedClass 클래스는 추상 클래스 BaseClass에서 파생됩니다. 추상 클래스에는 추상 메서드 AbstractMethod 및 두 개의 추상 속성 XY가 포함됩니다.

// Abstract class
abstract class BaseClass
{
    protected int _x = 100;
    protected int _y = 150;

    // Abstract method
    public abstract void AbstractMethod();

    // Abstract properties
    public abstract int X { get; }
    public abstract int Y { get; }
}

class DerivedClass : BaseClass
{
    public override void AbstractMethod()
    {
        _x++;
        _y++;
    }

    public override int X   // overriding property
    {
        get
        {
            return _x + 10;
        }
    }

    public override int Y   // overriding property
    {
        get
        {
            return _y + 10;
        }
    }

    static void Main()
    {
        var o = new DerivedClass();
        o.AbstractMethod();
        Console.WriteLine($"x = {o.X}, y = {o.Y}");
    }
}
// Output: x = 111, y = 161

앞의 예제에서 다음과 같이 문을 사용하여 추상 클래스를 인스턴스화하려고 하면,

BaseClass bc = new BaseClass();   // Error  

컴파일러가 추상 클래스 'BaseClass'의 인스턴스를 만들 수 없다는 오류가 표시됩니다.

그럼에도 불구하고 아래 예제와 같이 추상 클래스 생성자를 사용할 수 있습니다.

예제 4

public abstract class Shape
{
    public string Color { get; set; }

    // Constructor of the abstract class
    protected Shape(string color)
    {
        Color = color;
        Console.WriteLine($"Created a shape with color {color}.");
    }

    // Abstract method that must be implemented by derived classes
    public abstract double CalculateArea();
}

public class Square : Shape
{
    public double Side { get; set; }

    // Constructor of the derived class calling the base class constructor
    public Square(string color, double side) : base(color)
    {
        Side = side;
    }

    public override double CalculateArea()
    {
        return Side * Side;
    }
}

public class Program
{
    public static void Main(string[] args)
     {
            Square square = new Square("red", 5);
            Console.WriteLine($"Area of the square: {square.CalculateArea()}");            
     }
}

Shape 클래스는 abstract선언되므로 직접 인스턴스화할 수 없습니다. 대신 다른 클래스의 청사진 역할을 합니다.

  • 추상 클래스의 개체를 만들 수는 없지만 생성자가 있을 수 있습니다. 이 생성자는 보통 protected이며, 파생 클래스에서만 액세스할 수 있습니다. 이 경우 Shape 생성자는 color 매개 변수를 사용하고 Color 속성을 초기화합니다. 또한 콘솔에 메시지를 출력합니다. public Square(string color, double side) : base(color) 파트는 기본 클래스의 생성자(Shape)를 호출하고 color 인수를 전달합니다.
  • Shape 클래스에서 정의된 생성자는 색을 매개 변수 protected Shape(string color)사용합니다. 즉, C#에서 자동으로 제공되는 기본 매개 변수 없는 생성자가 더 이상 없으므로 파생 클래스는 : base(color) 식을 사용하여 기본 생성자를 호출해야 합니다. 기본값을 색 protected Shape(string color="green") 설정하면 파생 클래스에서 : base(color) 식을 생략할 수 있지만 이러한 생성자 protected Shape(string color="green") 호출되어 색을 녹색으로 설정합니다.

C# 언어 사양

자세한 내용은 C# 언어 사양을 참조하세요. 언어 사양은 C# 구문 및 사용법에 대 한 신뢰할 수 있는 소스 됩니다.

참고 항목