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
한줄 평 + 개선점
- 이제 슬슬 새로운 개념들이나 기존과 조금씩 다른 개념들이 나와서 신기하고 재밌다!