C# 문법 3주차 - 클래스

Amberjack·2024년 1월 2일
0

C# 문법

목록 보기
17/44

객체 지향 프로그래밍(OOP)의 특징

특징

  • 캡슐화(Encapsulation):

    • 관련된 데이터와 기능을 하나의 단위로 묶는 것!
    • 클래스를 사용하여 데이터와 메서드를 캡슐화하여 정보를 은닉, 접근을 제한함으로써 안정성과 유지보수성을 높인다.
  • 상속(Inheritance):

    • 상속은 기존의 클래스를 확장하여 새로운 클래스를 만드는 것!
    • 부모 클래스의 특성과 동작을 자식 클래스가 상속받아 재사용할 수 있다.
    • 코드의 중복을 줄이고, 클래스 간 계층 구조를 구성하여 코드의 구조화, 유지보수를 용이.
  • 다형성(Polymorphism):
    • 다형성은 하나의 인터페이스나 기능을 다양한 방식으로 구현하거나 사용할 수 있는 능력.
    • 하나의 메서드 이름이 다양한 객체에서 다르게 동작할 수 있는 것! 오버로딩, 오버라이딩을 통해 구현.
    • 유연하고 확장 가능한 코드 작성을 가능케 하며, 코드의 가독성과 재사용성을 높인다.
  • 추상화(Abstraction):
    • 클래스나 인터페이스를 사용하여 실제 세계의 개념을 모델링, 필요한 부분을 정의.
    • 세부 구현 내용을 감추고 핵심 개념에 집중함으로써 코드의 이해, 유지보수 용이.
  • 객체(Object):
    • 객체는 클리스로부터 생성된 실체. 데이터와 해당 데이터를 조작하는 메서드를 가지고 있다.
    • 객체는 상태(데이터)와 행동(메서드)를 가지며, 실제 세계의 개체가 개념을 모델링한다.
    • 객체들 간의 상호작용을 통해 프로그램이 동작, 모듈화와 재사용성 높인다.

클래스의 구성 요소

  • 필드(Fields): 클래스에서 사용되는 변수. 객체의 상태를 나타내는 데이터를 저장한다.
  • 메서드(Methods): 클래스에서 수행되는 동작을 정의. 객체의 동작을 구현하기 위해 사용.
  • 생성자(Constructors): 객체를 초기화하는 역할. 객체가 생성될 때 자동으로 호출, 필드를 초기화한다.
  • 소멸자(Destructors): 객체가 소멸될 때 호출되는 메서드. 메모리나 리소스의 해제 등의 작업 진행.

클래스

  • 클래스는 객체를 생성하기 위한 템플릿 또는 설계도 역할을 한다.
  • 클래스는 속성과 동작을 가진다. 속성은 필드, 동작은 메서드로 표현된다.
  • 객체를 생성하기 위해서는 클래스를 사용하여 인스턴스를 만들어야 한다.

→ 붕어빵을 만들기 위한 붕어빵 틀이라 생각하면 좋다.

객체

  • 객체는 클래스의 인스턴스. 클래스의 실체화된 형태.
  • 객체는 클래스로부터 생성, 각 객체는 독립적인 상태를 가지고 있다. → 객체마다 고유한 데이터를 가질 수 있다

→ 붕어빵 틀로 비교하면, 객체는 붕어빵이라 할 수 있다. 붕어빵 틀(class)로 만들어진 실제 붕어빵(객체)

클래서 선언과 인스턴스

클래스는 데이터와 메서드를 하나로 묶은 사용자 정의 타입.

class Person
{
    public string Name;
    public int Age;

    public void PrintInfo()
    {
        Console.WriteLine("Name: " + Name);
        Console.WriteLine("Age: " + Age);
    }
}

Person p = new Person();
p.Name = "John";
p.Age = 30;
p.PrintInfo(); // 출력: Name: John, Age: 30

👉 구조체 vs 클래스!!!

  • 구조체와 클래스 모두 사용자 정의 형식을 만드는 데 사용될 수 있다.
  • 구조체는 값 형식, 스택에 할당되고 복사될 때 값이 복사된다.
  • 클래스는 참조 형식, 힙에 할당되고 참조로 전달되므로 성능 측면에서 차이가 있다(동적 할당).
  • 구조체는 상속을 받을 수 없지만, 클래스는 단일, 다중 상속이 가능하다.
  • 구조체는 작은 크기의 데이터 저장, 단순한 데이터 구조에 적합, 클래스는 더 복잡한 객체를 표현하고 다양한 기능을 제공하기 위해 사용된다.
// 구조체를 생성하기
Person person1;
person1.Name = "John";
person1.Age = 25;
person1.PrintInfo();

// 클래스를 생성하기
Person p = new Person();
p.Name = "John";
p.Age = 25;
p.PrintInfo(); // 출력: Name: John, Age: 25

위의 코드에서 볼 수 있듯이 구조체는 값 형식이기 때문에 Person person1;의 형태로 선언을 한다.

반면 클래스의 경우, 참조 형식이기 때문에 Person p = new Person();와 같이 new를 사용하여 선언하는 것을 볼 수 있다(동적 할당).

구조체의 경우, person1이라는 변수가 실제로 생성이 되지만 클래스는 실제 Person의 주소값을 가지는 p를 선언한다.

접근 제한자(Access Modifier)

클래스의 접근 권한을 지정하여 데이터를 보호할 수 있다.

접근 제한자?

접근 제한자는 클래스, 필드, 메서드 등의 접근 가능한 범위를 지정하는 키워드.
접근 제한자는 클래스의 캡슐화를 제어하는 역할을 한다.

  • public : 외부에서 자유롭게 접근이 가능.
  • private : 같은 클래스 내부에서만 접근 가능.
  • protected : 같은 클래스 내부와 상속받은 클래스에서만 접근 가능.
class Person
{
    public string Name;         // 외부에서 자유롭게 접근 가능
    private int Age;           // 같은 클래스 내부에서만 접근 가능
    protected string Address;  // 같은 클래스 내부와 상속받은 클래스에서만 접근 가능
}

필드와 메서드

  • 클래스는 필드와 메서드로 구성된다.
  • 필드는 클래스 내부에 선언되어 있는 변수. 클래스의 상태를 나타내는 데이터를 저장.
  • 메서드는 클래스 내부에 선언되어 있는 함수. 클래스의 동작을 정의하고 실행.

필드(Fields)

  • 필드는 클래스나 구조체 내에서 객체의 상태를 저장하는 변수.
  • 객체의 특징, 속성을 표현하며, 클래스의 멤버 변수로 선언된다.
  • 보통 private 접근 제한자를 사용하여 외부의 접근을 제한하고, 필요한 경우 프로퍼티를 통해 간접적으로 접근하도록 한다.
class Player
{
    // 필드 선언
    private string name;
    private int level;
}

메서드(Methods)

  • 메서드는 클래스나 구조체에서 객체의 동작(기능)을 정의하는 함수.
  • 객체의 행동, 동작을 구현하며, 클래스의 멤버 함수로 선언한다.
  • 메서드는 입력값을 받아 처리하고 결과값을 반환할 수도 있다.
  • 객체의 상태(필드)를 변경하거나 다른 메서드를 호출하여 작업을 수행.
  • 보통 public 접근 제한자를 사용하여 외부에서 호출할 수 있도록 한다.
class Player
{
    // 필드
    private string name;
    private int level;

    // 메서드
    public void Attack()
    {
        // 공격 동작 구현
    }
}
  • 메서드를 호출하기 위해서는 해당 메서드가 속해 있는 클래스의 인스턴스를 생성해야 한다.
    Player player = new Player();  // Player 클래스의 인스턴스 생성
    player.Attack();  // Attack 메서드 호출

생성자와 소멸자

생성자(Constructor)

생성자?

  • 생성자는 객체가 생성될 때 호출되는 특별한 메서드.
  • 클래스의 인스턴스(객체)를 초기화하고, 필요한 초기값을 설정하는 역할.
  • 생성자는 클래스와 동일한 이름을 가지며, 반환 타입은 없다.
  • 객체를 생성할 때 new 키워드와 함께 호출된다.

생성자의 특징

  • 객체를 초기화하는 과정에서 필요한 작업을 수행할 수 있다.
  • 생성자는 여러 개 정의할 수 있으며, 매개변수의 개수, 타입에 따라 다른 생성자를 호출할 수 있다.
    👉 생성자 오버로딩!!!
  • 기본적으로 매개변수가 없는 디폴트 생성자가 자동 생성되지만, 사용자가 직접 정의한 생성자가 있는 경우 디폴트 생성자는 자동으로 생성되지 않는다.
    // 아무런 생성자 선언 없이 클래스 선언
    class Person
    {
        private string name;
        private int age;
    
        public void PrintInfo()
        {
            Console.WriteLine($"Name: {name}, Age: {age}");
        }
    }
    
    
    Person person1 = new Person();

    해당 코드에는 눈에 보이지 않는 디폴트 생성자가 생성되어 있다.

    public Person()
    {
    
    }

    와 같은 형식의 생성자가 자동으로 생성되게 된다. 이 생성자는 new Person()과 같다.

// 생성자 오버로딩을 하는 경우
class Person
{
    private string name;
    private int age;

    // 매개변수가 없는 디폴트 생성자
    public Person()
    {
        name = "Unknown";
        age = 0;
    }

    // 매개변수를 받는 생성자
    public Person(string newName, int newAge)
    {
        name = newName;
        age = newAge;
    }

    public void PrintInfo()
    {
        Console.WriteLine($"Name: {name}, Age: {age}");
    }
}

Person person1 = new Person();                     // 디폴트 생성자 호출
Person person2 = new Person("John", 25);           // 매개변수를 받는 생성자 호출

소멸자(Destructor)

소멸자?

  • 소멸자는 객체가 소멸되는 시점에서 자동으로 호출된다.
  • 객체의 사용이 종료되고 메모리에서 해제될 때 자동으로 호출되어 필요한 정리 작업을 수행한다.
  • 클래스와 동일한 이름을 가지며, 이름 앞에 ~ 기호를 붙여 표현한다.
  • 소멸자는 반환 타입이 없고 매개변수를 가질 수 없다.
  • C#에서는 가비지 컬렉터에 의해 관리되는 메모리 해제를 담당하므로 명시적으로 소멸자를 호출하는 것은 일반적으로 권장되지 않는다!!!

소멸자의 역할

  • 자원 해제: 파일 핸들, 네트워크 연결, 데이터베이스 연결 등의 외부 리소스를 사용한 경우, 소멸자를 통해 해당 리소스를 해제할 수 있다.
  • 메모리 해제: 객체가 사용한 메모리를 해제하고 관련된 자원을 정리할 수 있다.
  • 로깅 및 디버깅: 객체가 소멸되는 시점에 로깅 작업을 수행하거나 디버깅 정보를 기록할 수 있다.
class Person
{
    private string name;

    public Person(string newName)
    {
        name = newName;
        Console.WriteLine("Person 객체 생성");
    }

    ~Person()
    {
        Console.WriteLine("Person 객체 소멸");	// C#의 버전이 올라감에 따라 프로그램이 종료될 때 소멸자 호출을 보여주지 않는다고 함...
    }
}

프로퍼티(Property)

프로퍼티?

  • 프로퍼티는 클래스 멤버로서, 객체의 필드 값을 읽거나(get) 설정하는데(set) 사용되는 접근자 메서드의 조합.
  • 객체의 필드에 직접 접근하지 않고, 간접적으로 값을 설정하거나 읽을 수 있도록 한다.
  • 필드에 대한 접근 제어와 데이터 유효성 검사 등을 수행할 수 있다.
  • 프로퍼티는 필드와 마찬가지로 객체의 상태를 나타내는 데이터 역할을 하지만, 외부에서 접근할 때 추가적인 로직을 수행할 수 있다.

프로퍼티 구문

  • 프로퍼티는 get과 set 접근자를 사용하여 값을 읽고 설정하는 동작을 정의한다.
  • get은 프로퍼티의 값을 반환하고, set은 프로퍼티의 값을 설정한다.
  • 필요에 따라 get 또는 set 접근자 중 하나를 생략하여 읽기 전용 또는 쓰기 전용 프로퍼티를 정의할 수 있다.
[접근 제한자] [데이터 타입] 프로퍼티명
{
    get
    {
        // 필드를 반환하거나 다른 로직 수행
    }
    set
    {
        // 필드에 값을 설정하거나 다른 로직 수행
    }
}

프로퍼티 사용 예시

class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set { age = value; }
    }
}


Person person = new Person();
person.Name = "John";   // Name 프로퍼티에 값 설정(Name의 set이 호출됨)
person.Age = 25;        // Age 프로퍼티에 값 설정(Age의 set이 호출됨)

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // Name과 Age 프로퍼티(Name, Age의 get이 호출됨)

프로퍼티 접근 제한자 적용 & 유효성 검사 예제

class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        private set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set
        {
        	// 나이에 0 또는 음수 값이 설정될 경우를 방지할 수 있다 → 유효성 검사
            if (value >= 0)
                age = value;
        }
    }
}


Person person = new Person();
person.Name = "John";     // 컴파일 오류: Name 프로퍼티의 set 접근자는 private입니다.
person.Age = -10;         // 유효성 검사에 의해 나이 값이 설정되지 않습니다.

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // Name과 Age 프로퍼티에 접근하여 값을 출력합니다.

자동 프로퍼티(Auto Property)

  • 자동 프로퍼티는 프로퍼티를 간단하게 정의하고 사용할 수 있는 편리한 기능.
  • 필드의 선언과 접근자 메서드의 구현을 컴파일러가 자동으로 처리한다.
[접근 제한자] [데이터 타입] 프로퍼티명 { get; set; }

ex)

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}


Person person = new Person();
person.Name = "John";     // 값을 설정
person.Age = 25;          // 값을 설정

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // 값을 읽어 출력

0개의 댓글