C#/Etc

복합형 (Complex Types) - class, struct, array, Enum, Tuple, Record, Delegate, Interface 등

yum-yum_IT 2024. 1. 4.
반응형

안녕하세요. 
오늘은 데이터 타입(Data Type) - 복합형 (Complex Types)을 소개 하겠습니다.

난해한 부분이 있다면 예시 코드를 직접 입력해보시는 것을 권장합니다. 코드를 직접 타이핑해보는 행위 자체가 소중한 학습 경험이 될 수 있습니다.

더 많은 문법을 알고 싶으시다면 이 사이트를 참고하시면 됩니다. 

2023.12.25 - [C#/News] - C# 기초 문법(기본 개념) Yum-yum

 

 

복합형 (Complex Types)

1. 복합형(Complex Types)이란?

 : 여러 데이터를 하나의 단위로 묶어서 관리할 수 있는 타입을 의미합니다. C#에서 복합 데이터 타입에는 다음과 같은 것들이 있습니다.

  • 클래스(Class)
  • 구조체(Struct)
  • 인터페이스(Interface)
  • 배열(Array)
  • 열거형(Enum)
  • 델리게이트(Delegate)
  • 튜플(Tuple)
  • 레코드(Record)

 

 ◎ 클래스(Class)

 : C#에서 클래스(Class)는 객체 지향 프로그래밍의 핵심으로, 데이터와 이를 처리하는 메서드를 결합한 사용자 정의 타입입니다. 클래스는 상태를 나타내는 필드(변수)와 행동을 정의하는 메서드(함수)로 구성됩니다. 클래스를 기반으로 생성된 인스턴스를 객체(Object)라고 하며, 각 객체는 독립된 상태를 가지고 클래스에 정의된 행동을 수행할 수 있습니다. 

클래스는 다음과 같은 특징을 가집니다.

  • 캡슐화(Encapsulation): 데이터와 데이터를 조작하는 메서드를 하나로 묶어서 외부의 접근으로부터 보호합니다.
  • 상속(Inheritance): 한 클래스가 다른 클래스의 속성과 메서드를 물려받을 수 있습니다.
  • 다형성(Polymorphism): 같은 이름의 메서드가 다른 행동을 할 수 있도록 하는 기능입니다.

 

 ▶ 클래스(Class) Code 예제

using System;

// 'Person' 클래스 정의
public class Person
{
    // 필드(상태)
    public string Name;
    public int Age;

    // 생성자
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    // 메서드(행동)
    public void Introduce()
    {
        Console.WriteLine($"안녕하세요, 제 이름은 {Name}이고, 나이는 {Age}살 입니다.");
    }
}

class Program
{
    static void Main()
    {
        // 'Person' 클래스의 인스턴스 생성
        Person person = new Person("홍길동", 30);

        // 'Introduce' 메서드 호출
        person.Introduce();
    }
}

 >> 위 예제에서 Person 클래스는 Name과 Age라는 두 개의 필드를 가지고 있고, Introduce라는 메서드를 통해 자기소개를 할 수 있습니다. Main 메서드에서는 Person 객체를 생성하고 Introduce 메서드를 호출하여 자기소개를 출력합니다.

 

 ◎ 구조체(Struct)

 : C#에서 구조체(Struct)는 클래스와 유사하지만 몇 가지 중요한 차이점이 있는 값 타입(Value Type)입니다. 구조체는 일반적으로 작은 데이터 그룹을 표현할 때 사용되며, 클래스보다 가볍고 스택 메모리에 할당됩니다. 상속은 받을 수 없지만, 인터페이스는 구현할 수 있습니다.
구조체의 특징은 다음과 같습니다:

  • 값 타입이므로, 구조체의 인스턴스는 직접 스택에 저장되며, 할당과 복사 시에 해당 인스턴스의 실제 값이 복사됩니다.
  • 구조체는 상속을 지원하지 않지만, 인터페이스를 구현할 수 있습니다.
  • 일반적으로 소량의 데이터를 다루는 경우에 사용되며, 메서드, 속성, 인덱서 등을 포함할 수 있습니다.

 

 ▶ 구조체(Struct) Code 예제

using System;

// 'Point' 구조체 정의
public struct Point
{
    // 필드(상태)
    public int X;
    public int Y;

    // 생성자
    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    // 메서드(행동)
    public void Display()
    {
        Console.WriteLine($"X: {X}, Y: {Y}");
    }
}

class Program
{
    static void Main()
    {
        // 'Point' 구조체의 인스턴스 생성
        Point point = new Point(10, 20);

        // 'Display' 메서드 호출
        point.Display();
    }
}

 >> 위 예제에서 Point 구조체는 두 개의 정수형 필드 X와 Y를 가지고 있으며, 이를 초기화하는 생성자와 해당 포인트를 출력하는 Display 메서드를 가지고 있습니다. Main 메서드에서는 Point 구조체의 새 인스턴스를 만들고, Display 메서드를 호출하여 좌표를 출력합니다.

 

 ◎ 인터페이스(Interface)

 : C#에서 인터페이스(Interface)는 메서드, 속성, 이벤트, 인덱서 등의 멤버의 시그니처만을 선언하는 참조 타입입니다. 인터페이스는 어떤 클래스나 구조체가 되어야 할 행위의 '계약'을 정의하며, 그 계약을 구현하는 클래스나 구조체는 인터페이스에 선언된 모든 멤버를 구현해야 합니다. 인터페이스는 자체적으로 어떠한 구현도 포함하지 않으며, 다중 상속의 이점을 제공합니다.
인터페이스의 특징은 다음과 같습니다:

  • 인터페이스는 메서드, 속성, 이벤트 등의 정의만 가지고 있으며, 구현은 가지고 있지 않습니다.
  • 하나의 클래스나 구조체가 여러 인터페이스를 구현할 수 있습니다.
  • 인터페이스를 통해 다형성을 실현할 수 있습니다.

 

 ▶ 인터페이스(Interface) Code 예제

using System;

// 'IMovable' 인터페이스 정의
public interface IMovable
{
    void Move(int distance);
}

// 'Car' 클래스가 'IMovable' 인터페이스를 구현
public class Car : IMovable
{
    public void Move(int distance)
    {
        Console.WriteLine($"차량이 {distance} 미터 만큼 움직였습니다.");
    }
}

class Program
{
    static void Main()
    {
        // 'Car' 클래스의 인스턴스 생성
        Car car = new Car();

        // 인터페이스를 통해 'Move' 메서드 호출
        car.Move(100);
    }
}

>> 위 예제에서 IMovable 인터페이스는 Move 메서드를 선언하고 있으며, Car 클래스는 이 인터페이스를 구현하기 위해 Move 메서드를 정의하고 있습니다. 프로그램이 실행되면 Car 객체를 통해 Move 메서드가 호출되고, "차량이 100 미터 만큼 움직였습니다."라는 메시지가 출력됩니다.

 

 ◎ 배열(Array)

 : C#에서 복합형(Complex Types) 중 하나인 배열(Array)은 동일한 타입의 여러 변수를 하나의 이름으로 관리할 수 있게 해주는 데이터 구조입니다. 배열은 연속적인 메모리 공간에 요소들을 순차적으로 저장하며, 각 요소는 인덱스를 사용하여 접근할 수 있습니다.
예를 들어, int 타입의 배열은 여러 개의 정수를 저장할 수 있고, 각 정수는 배열 내에서 고유한 위치(인덱스)를 가집니다. 배열의 인덱스는 0부터 시작합니다. 즉, 첫 번째 요소는 인덱스 0에, 두 번째 요소는 인덱스 1에 위치하게 됩니다.

 

 ▶ 배열(Array) Code 예제

using System;

class Program
{
    static void Main()
    {
        // int 타입의 배열을 선언하고 초기화합니다.
        int[] numbers = new int[5] { 1, 2, 3, 4, 5 };

        // 배열에 저장된 요소들을 출력합니다.
        for (int i = 0; i < numbers.Length; i++)
        {
            Console.WriteLine(numbers[i]);
        }

        // 배열의 특정 요소에 접근하여 값을 변경합니다.
        numbers[2] = 10;

        // 변경된 배열을 출력합니다.
        foreach (int number in numbers)
        {
            Console.WriteLine(number);
        }
    }
}

 >> 위 예제에서는 다섯 개의 정수를 저장할 수 있는 numbers라는 이름의 배열을 선언하고 초기화했습니다. for 루프와 foreach 루프를 사용하여 배열의 모든 요소를 출력했습니다. 또한, 배열의 세 번째 요소(인덱스 2)의 값을 10으로 변경했습니다.

 

 ◎ 열거형(Enum)

 : C#에서 열거형(Enum)은 관련된 상수들의 집합을 정의하는 사용자 정의 값 타입입니다. 열거형을 사용하면 숫자 대신에 의미 있는 이름을 코드에 포함시켜 가독성을 높이고, 오류를 줄일 수 있습니다. 기본적으로 각 열거형 멤버의 기본 형식은 int이며, 다른 정수형으로 명시적으로 변경할 수 있습니다.

 

 ▶ 열거형(Enum) Code 예제

using System;

// 'Days' 열거형 정의
public enum Days
{
    Sunday,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}

class Program
{
    static void Main()
    {
        // 'Days' 열거형의 인스턴스 생성
        Days today = Days.Wednesday;

        // 'today' 값 출력
        Console.WriteLine(today);  // 출력: Wednesday

        // 열거형 멤버의 정수 값 출력
        Console.WriteLine((int)today);  // 출력: 3 (기본적으로 Sunday가 0부터 시작)
    }
}

 >> 위 예제에서 Days라는 이름의 열거형을 정의하고 있으며, 요일을 나타내는 일곱 개의 멤버를 가지고 있습니다. 프로그램에서는 Days 열거형을 사용하여 today라는 변수에 Days.Wednesday 값을 할당하고 있습니다. 그리고 Console.WriteLine을 사용하여 today의 이름과 해당하는 정수 값을 출력합니다.
열거형 멤버에 특정 값을 할당하지 않으면, 기본적으로 첫 번째 멤버는 0의 값을 가지며, 이후의 멤버들은 자동으로 1씩 증가된 값을 가집니다. 필요한 경우, 멤버에 특정 값을 명시적으로 할당할 수도 있습니다.

 

 ◎ 델리게이트(Delegate)

 : C#에서 델리게이트(Delegate)는 메서드에 대한 참조를 보관하는 타입입니다. 즉, 델리게이트는 하나 이상의 메서드를 가리키는 객체로, 메서드를 변수처럼 전달하고 할당할 수 있게 해줍니다. 이는 메서드를 인자로 넘기거나 콜백(callback)으로 사용할 때 매우 유용합니다.
델리게이트는 다음과 같은 상황에서 사용됩니다:

  • 이벤트 처리(Event handling)
  • 콜백 메서드 정의
  • LINQ와 같은 기능에서 람다 표현식과 함께 사용
  • 스레드 프로그래밍에서 쓰레드에 전달할 메서드 지정

 

 ▶ 델리게이트(Delegate) Code 예제

using System;

// 델리게이트 선언: 이 델리게이트는 반환 타입이 void이고 정수 하나를 매개변수로 받는 메서드를 참조할 수 있습니다.
public delegate void MyDelegate(int a);

class Program
{
    public static void Main()
    {
        // 델리게이트 인스턴스 생성하면서, 참조할 메서드를 지정합니다.
        MyDelegate del = new MyDelegate(MethodA);
        
        // 델리게이트를 통해 메서드를 호출합니다.
        del(50);

        // 델리게이트에 다른 메서드를 할당합니다.
        del = MethodB;
        
        // 델리게이트를 통해 다른 메서드를 호출합니다.
        del(25);
    }

    // MyDelegate 타입의 델리게이트가 참조할 수 있는 메서드
    public static void MethodA(int a)
    {
        Console.WriteLine("MethodA called with: " + a);
    }

    // MyDelegate 타입의 델리게이트가 참조할 수 있는 또 다른 메서드
    public static void MethodB(int b)
    {
        Console.WriteLine("MethodB called with: " + b);
    }
}

>> 위 예제에서 MyDelegate라는 이름의 델리게이트 타입을 정의했습니다. 이 델리게이트는 매개변수로 정수를 하나 받고 반환 값이 없는(void) 메서드를 참조할 수 있습니다. MethodA와 MethodB라는 두 개의 메서드를 만들고, 이를 MyDelegate 타입의 델리게이트 인스턴스에 할당하여 호출하는 방식을 보여줍니다.
델리게이트는 C# 프로그래밍에서 강력한 기능을 제공하며, 특히 이벤트와 콜백을 다룰 때 유용합니다.

 

 ◎ 튜플(Tuple)

 : C#에서 튜플(Tuple)은 여러 필드를 포함할 수 있는 데이터 구조로, 각 필드는 서로 다른 타입을 가질 수 있습니다. 튜플은 메서드에서 여러 값을 반환하거나, 임시적으로 데이터 그룹을 만들 때 유용하게 사용됩니다. C# 7.0부터는 튜플을 더 쉽고 의미 있게 사용할 수 있는 새로운 문법이 도입되었습니다.
튜플의 특징은 다음과 같습니다:

  • 여러 필드를 담을 수 있으며, 각 필드는 서로 다른 타입일 수 있습니다.
  • 튜플은 익명 타입으로, 필드에 대한 명시적인 이름을 주지 않아도 됩니다.
  • 필드에 명시적인 이름을 부여하여 가독성을 높일 수 있습니다.
  • 메서드에서 하나 이상의 값을 반환할 때 유용합니다.

 

 ▶ 튜플(Tuple) Code 예제

using System;

class Program
{
    static void Main()
    {
        // 튜플 생성 및 초기화
        var person = (Name: "홍길동", Age: 30);

        // 튜플의 요소에 접근
        Console.WriteLine($"이름: {person.Name}, 나이: {person.Age}");

        // 메서드에서 튜플 반환받기
        var (name, age) = GetPersonInfo();
        Console.WriteLine($"이름: {name}, 나이: {age}");
    }
    
    // 튜플을 반환하는 메서드
    static (string Name, int Age) GetPersonInfo()
    {
        // 튜플 리터럴을 사용한 반환
        return ("김철수", 25);
    }
}

 >> 위 예제에서는 person이라는 튜플 변수를 생성하고, 이름과 나이를 가지고 있습니다. 이 튜플은 Name과 Age라는 이름으로 각 필드에 접근할 수 있습니다. 또한 GetPersonInfo 메서드는 이름과 나이를 포함한 튜플을 반환하고, 메서드를 호출할 때 반환된 값을 분해하여 각각 name과 age 변수에 할당합니다.

 

 ◎ 레코드(Record)

 : C#에서 레코드(Record)는 C# 9.0부터 도입된 참조 타입으로, 주로 불변성을 가지는 객체를 만들기 위해 사용됩니다. 클래스와 비슷하게 데이터를 캡슐화하고 메서드를 포함할 수 있지만, 레코드는 값 기반의 동등성 비교를 기본으로 제공합니다. 즉, 레코드의 인스턴스가 동일한지 비교할 때는 인스턴스의 참조가 아닌 내부에 저장된 값들을 비교합니다.
레코드는 데이터 전송 객체(Data Transfer Objects, DTOs), POCOs(Plain Old CLR Objects), 그리고 다른 간단한 객체들을 정의할 때 유용하게 사용됩니다.

 

 ▶ 레코드(Record) Code 예제

using System;

// 레코드 정의
public record Person(string FirstName, string LastName);

class Program
{
    static void Main(string[] args)
    {
        // 레코드 인스턴스 생성
        var person1 = new Person("John", "Doe");
        var person2 = new Person("John", "Doe");

        // 레코드 인스턴스 출력
        Console.WriteLine(person1); // 출력: Person { FirstName = John, LastName = Doe }

        // 값 기반의 동등성 비교
        Console.WriteLine(person1 == person2); // 출력: True

        // 레코드의 with 표현식을 사용하여 복사본 생성 및 속성 변경
        var person3 = person1 with { FirstName = "Jane" };

        // 변경된 레코드 인스턴스 출력
        Console.WriteLine(person3); // 출력: Person { FirstName = Jane, LastName = Doe }
    }
}

 >> 위 예제에서 Person이라는 레코드 타입을 정의하였고, FirstName과 LastName이라는 두 개의 속성을 가집니다. person1과 person2는 동일한 값을 가지므로, == 연산자로 비교했을 때 True를 반환합니다. with 표현식을 사용하여 person1의 복사본을 만들면서 FirstName을 "Jane"으로 변경하여 새로운 레코드 인스턴스 person3를 생성합니다.

 

//END

반응형

댓글