C# [3주차]

  • 이번 회차에서는 C#의 클래스와 객체에 대해 설명하고 있다.

  • 저번과 같이 중복되어 알고 있었던 부분보다 새롭게 알게되거나 개념이 달라진 부분 위주로 작성할 계획이다!

생성자(Constructor)와 소멸자(Destructor)

객체가 생성될 때 호출되는 특별한 메서드로 Node.js의 Constructor와 같은 개념이다! 조금 다른게 있다면,
오버로딩이나 초기화 시 메서드 이름클래스 이름으로 해줘야된다는 것 정도..?

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}");
    }
}

소멸자는 객체가 소멸되는 시점에서 자동으로 호출되는 특별한 메서드로 이번에 새롭게 알게된 개념이다!
(Node.js 에선 소멸하기 전에 초기화/추가작업 을 해주는 메서드들을 직접 불러야 했는데..)

    // Class 이름 앞에 ~ 를 붙여 메서드를 정의해주면 된다!
    ~Person() {
        Console.WriteLine("Person 객체 소멸");
    }

프로퍼티(Property)

객체의 필드 값을 읽거나 설정하는데 사용되는 접근자(Accessor) 메서드를 의미한다!
( Get / Set 지정해주는 거자너 )

  • 만약 프로퍼티를 설정해주지 않으면 기본적으로 Internal 접근제한자가 적용된다!
[접근 제한자] [데이터 타입] 프로퍼티명
{
    [접근 제한자] get {
        // 필드를 반환하거나 다른 로직 수행
    }
    [접근 제한자] set {
        // 필드에 값을 설정하거나 다른 로직 수행
    }
}

// 자동 프로퍼티(Auto Property)
[접근 제한자] [데이터 타입] 프로퍼티명 { get; set; }

상속

기존의 클래스(부모 클래스 또는 상위 클래스)를 확장하거나 재사용하여 새로운 클래스(자식 클래스 또는 하위 클래스)를 생성하는 것으로 기존의 Node.js extend와 다른 부분만 조금 확인해볼 예정이다.

  • 인터페이스 다중 상속

    • 클래스는 하나의 클래스여러 개의 인터페이스동시에 상속받을 수 있음
  • 가상(Virtual) 메서드

    • 부모 클래스에서 정의되고 자식 클래스에서 재정의할 수 있는 메서드

    • 부모 클래스의 타입을 사용하여 자식들을 저장하고 메서드에 접근할 경우, 부모의 메서드가 호출되는 경우를 방지
      ( 오버라이드가 되어있는 메서드임에도 부모 타입으로 저장되어있으면 무시됨 )

  • 추상(Abstract) 클래스 / 메서드

    • 직접적으로 인스턴스를 생성할 수 없는 클래스로 상속을 위한 베이스 클래스로 사용됨

    • 자식 클래스에서 반드시 구현되어야 함

// 부모 클래스
public class Animal
{
    public string Name { get; set; }
    public int Age { get; set; }

    public abstract void Draw();

    // 가상 메서드
    public virtual void Eat() {
        Console.WriteLine("Animal is eating.");
    }
}

// 상속받은 자식 클래스 
public class Dog : Animal
{
    public override void Eat() {
        Console.WriteLine("Dog is eating.");
    }

    public override void Draw()
    {
        Console.WriteLine("Drawing a poop");
    }
}

// virtual/override 사용 예시
List<Animal> list = new List<Animal>();
list.Add(new Dog());

// 이 경우 Animal의 Eat()이 우선순위로 사용되나, virtual 에 의해 자식의 메서드가 사용된다!
foreach (Animal animal in list) {
    animal.Eat();
}

제너릭

클래스나 메서드를 일반화시켜 다양한 자료형에 대응할 수 있는 기능 ( 약간 var 느낌인 거넹 )
C#에서는 <T> 형태의 키워드를 이용하여 제너릭을 선언해준다!

// 제너릭 클래스 선언 예시
class Stack<T>
{
    private T[] elements;
    private int top;

    public Stack() {
        elements = new T[100];
        top = 0;
    }

    public void Push(T item) {
        elements[top++] = item;
    }

    public T Pop() {
        return elements[--top];
    }
}
// 제너릭 클래스 사용 예시
Stack<int> intStack = new Stack<int>();
intStack.Push(1);
intStack.Push(5);
Console.WriteLine(intStack.Pop()); // 출력 결과: 5
  • 두 개 이상의 제너릭을 사용하는 경우 배열과 같이 , 를 사용해주면 된다!
class Pair<T1, T2>
{
    public T1 First { get; set; }
    public T2 Second { get; set; }

    public Pair(T1 first, T2 second) {
        First = first;
        Second = second;
    }

    public void Display() {
        Console.WriteLine($"First: {First}, Second: {Second}");
    }
}

Pair<int, string> pair1 = new Pair<int, string>(1, "One");
pair1.Display();

Out 과 Ref

메서드에서 매개변수를 전달할 때 사용해 메서드에서 값을 반환하는 것이 아니라, 매개변수를 이용하여 값을 전달할 수 있다!

  • out: 메서드에서 반환 값을 매개변수로 전달하는 경우에 사용
    (Out 매개변수는 메서드 내에서 반드시 값을 할당해야 한다!)

  • ref: 매개변수를 수정하여 원래 값에 영향을 주는 경우에 사용
    ( Ref는 값 복사 없이 직접 접근하기에 성능상 좋긴하나 가독성이 떨어진다..)

// out 키워드 사용 예시
void Divide(int a, int b, out int quotient, out int remainder) {
    quotient = a / b;
    remainder = a % b;
}

int quotient, remainder;
Divide(10, 5, out quotient, out remainder);
Console.WriteLine($"{quotient}, {remainder}"); // 출력 결과: 2, 0

// ref 키워드 사용 예시
void Swap(ref int a, ref int b) {
    int temp = a;
    a = b;
    b = temp;
}

int x = 1, y = 2;
Swap(ref x, ref y);
Console.WriteLine($"{x}, {y}"); // 출력 결과: 2, 1

한줄 평 + 개선점

  • 이제 슬슬 새로운 개념들이나 기존과 조금씩 다른 개념들이 나와서 신기하고 재밌다!