본문 바로가기

내일배움캠프_TIL

8월 16일 TIL

C#강의 2주차 내용 복습


배열

여러개의 변수가 같은 자료형일때 사용

배열의 1번째 인덱스 번호는 0부터 시작 → 배열의 첫번째 요소에 접근하려면

array1[0] = 1;

위와 같이 사용해야함

 

int[] itemPrices = { 100, 200, 300, 400, 500 };
int totalPrice = 0;

for (int i = 0; i < itemPrices.Length; i++)
{
    totalPrice += itemPrices[i];
}

Console.WriteLine("총 아이템 가격: " + totalPrice + " gold");

배열의 요소에 접근할 때는 하나씩 접근하지 않고 반복문과 인덱스 번호를 이용하여 접근

 

다차원 배열

// 2차원 배열의 선언과 초기화
int[,] array3 = new int[2, 3];  // 2행 3열의 int형 2차원 배열 선언

// 다차원 배열 초기화
array3[0, 0] = 1;
array3[0, 1] = 2;
array3[0, 2] = 3;
array3[1, 0] = 4;
array3[1, 1] = 5;
array3[1, 2] = 6;

// 선언과 함께 초기화
int[,] array2D = new int[3, 4] { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
// 3차원 배열의 선언과 초기화
int[,,] array3D = new int[2, 3, 4] 
{
    { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
    { { 13, 14, 15, 16 }, { 17, 18, 19, 20 }, { 21, 22, 23, 24 } }
};
[x0, y0]
1
[x1, y0]
2
[x2, y0]
3
[x0, y1]
4
[x1, y2]
5
[x2, y1]
6

여러개의 배열을 붙인 구조

2차원 배열 : 축이 2개인 표와 같은 구조 - 컴퓨터 안에서는 일렬로 생성됨

3차원 배열 : 축이 하나 추가되어 3개인 구조


컬렉션

배열과 비슷하지만 크기 변경이 자유롭다

 

List

List<int> numbers = new List<int>(); // 빈 리스트 생성
numbers.Add(1); // 리스트에 데이터 추가
numbers.Add(2);
numbers.Add(3);
numbers.Remove(2); // 리스트에서 데이터 삭제

foreach(int number in numbers) // 리스트 데이터 출력
{
    Console.WriteLine(number);
}

위 코드에서는 2를 지워서 1과 3만 출력된다

for문이 아니라 foreach문을 사용하는 이유는 컴퓨터에서 일렬로 이어서 데이터를 저장하는 배열과 달리 Length를 사용할 수 없기 때문

반복문을 사용하려면 Count를 사용해야 한다

 

Dictionary

using System.Collections.Generic;

Dictionary<string, int> scores = new Dictionary<string, int>(); // 빈 딕셔너리 생성
scores.Add("Alice", 100); // 딕셔너리에 데이터 추가
scores.Add("Bob", 80);
scores.Add("Charlie", 90);
scores.Remove("Bob"); // 딕셔너리에서 데이터 삭제

foreach(KeyValuePair<string, int> pair in scores) // 딕셔너리 데이터 출력
{
    Console.WriteLine(pair.Key + ": " + pair.Value);
}
밸류
A 100
B 80
C 90

키와 밸류가 쌍을 이루는 데이터 구조

위 코드에서는 Bob을 호출하는 것으로 80이라는 밸류를 삭제

 

Stack - 후입선출

Stack<int> stack1 = new Stack<int>();  // int형 Stack 선언

// Stack에 요소 추가
stack1.Push(1);
stack1.Push(2);
stack1.Push(3);

// Stack에서 요소 가져오기
int value = stack1.Pop(); // value = 3 (마지막에 추가된 요소)

Queue - 선입선출

Queue<int> queue1 = new Queue<int>(); // int형 Queue 선언

// Queue에 요소 추가
queue1.Enqueue(1);
queue1.Enqueue(2);
queue1.Enqueue(3);

// Queue에서 요소 가져오기
int value = queue1.Dequeue(); // value = 1 (가장 먼저 추가된 요소)

 

HashSet

HashSet<int> set1 = new HashSet<int>();  // int형 HashSet 선언

// HashSet에 요소 추가
set1.Add(1);
set1.Add(2);
set1.Add(3);

// HashSet에서 요소 가져오기
foreach (int element in set1)
{
    Console.WriteLine(element);
}

List와 비슷하지만 차이점은 List는 중복 요소가 존재하는 반면 HashSet은 중복되지 않는 요소들로만 이루어져 있음


배열과 리스트

리스트의 유연성만 믿고 배열을 사용할 수 있는 모든 상황을 리스트로 대체하는 것은 좋지 않다

  1. 메모리 사용량 - 리스트는 동적으로 길이를 조정할 수 있지만 그만큼 배열보다 많은 메모리를 사용하게 되므로 리스트를 많이 사용하면 성능이 떨어질 수 있다
  2. 데이터 접근 시간 증가 - 배열과 달리 리스트는 컴퓨터 안에서 정렬되어 있지 않기 때문에 순서대로 접근이 가능한 배열과 다르게 리스트의 특정 요소를 찾으려면 컴퓨터가 일일히 뒤져봐야 하기 때문에 데이터 접근에 지연이 생긴다
  3. 코드 복잡도 증가 - 리스트는  데이터의 추가 및 삭제 등이 자유롭지만 그만큼 사용되는 코드의 줄 수가 늘어나므로 코드를 읽는것과 관리하는것이 어려워진다

메서드

C언어에서 함수라고 하는 것을 C#에서는 메서드라고 함

메서드를 사용하는 이유

  • 같은 코드가 여러번 쓰일 때 메서드를 사용하면 더 간편하게 반복할 수 있음
  • 코드의 가독성이 올라가고 오류가 났을 때 메서드 안에서 고치면 해결되는 등 유지보수가 쉬워짐

메서드 사용법

 

[접근 제한자] [리턴 타입] [메서드 이름]([매개변수])
{
    // 메서드 실행 코드
}

접근제한자 : public, private 등 변수와 마찬가지로 접근할 수 있는 범위를 제한

리턴 타입 : int, float 등 메서드가 반환할 값의 타입을 지정 반환값이 없다면 void

메서드 이름 : 메서드를 호출할 때 사용될 이름 이미 C#내장 메서드인 Writeline 등이 여기에 해당한다

매개변수 : 메서드를 호출하면서 메서드에 전달할 값 0개 혹은 1개 이상으로 입력 가능

실행코드 : 메서드 이름과 매개변수를 입력하여 호출한 메서드가 실행할 코드의 내용

// 예시 1: 반환 값이 없는 메서드
public void SayHello()
{
    Console.WriteLine("안녕하세요!");
}

// 예시 2: 매개변수가 있는 메서드
public void GreetPerson(string name)
{
    Console.WriteLine("안녕하세요, " + name + "님!");
}

// 예시 3: 반환 값이 있는 메서드
public int AddNumbers(int a, int b)
{
    int sum = a + b;
    return sum;
}

호출 방법

[메서드 이름]([전달할 매개변수]);
Console.WriteLine(SayHello());

여태까지 사용한 Console.WriteLine이 이미 함수 이름과 매개변수를 사용하여 호출한 메서드다


메서드 오버로딩

같은 이름의 메서드를 매개변수의 타입, 순서, 개수 등이 다르면 이름이 같더라도 다른 메서드로 취급

void PrintMessage(string message)
{
    Console.WriteLine("Message: " + message);
}

void PrintMessage(int number)
{
    Console.WriteLine("Number: " + number);
}

// 메서드 호출
PrintMessage("Hello, World!");  // 문자열 매개변수를 가진 메서드 호출
PrintMessage(10);  // 정수 매개변수를 가진 메서드 호출

정리 하는 중 생긴 궁금증 : Console.WriteLine도 매개변수로 문자열과 숫자 다 사용 가능한데 그럼 Console.WriteLine도 메서드 오버로딩이 적용된 내장메서드인가?

ChatGPT 답변
비주얼 스튜디오에서 메서드 오버로딩된 메서드의 목록을 표시해준다

메서드 오버로딩은 반환값의 변화는 체크하지 않는다


재귀호출

void CountDown(int n)
{
    if (n <= 0)
    {
        Console.WriteLine("Done");
    }
    else
    {
        Console.WriteLine(n);
        CountDown(n - 1);  // 자기 자신을 호출
    }
}

// 메서드 호출
CountDown(5);

팩토리얼을 예시로 한 재귀함수

메서드가 자기 자신을 호출하는 것

재귀호출을 사용하여 더 쉽거나 간결하게 풀 수 있는 문제들이 있지만 잘못하면 무한루프에 빠지기 쉽기 때문에 사용에는 주의가 필요

재귀호출은 스택 방식을 따라간다

호출된 메서드의 정보를 스택에 순서대로 쌓고 메서드가 반환되면 쌓아놓은 스택에서 순차적으로 제거


구조체

struct Person
{
    public string Name;
    public int Age;

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

여러 변수들을 묶어서 하나로 만드는 사용자 정의 자료형

struct 키워드로 선언하고 구조체의 멤버는 변수와 메서드가 들어갈 수 있다

 

사용법

Person person1;
person1.Name = "John";
person1.Age = 25;
person1.PrintInfo();

변수 선언처럼 사용 가능하며 구조체 멤버에 접근할 때는 .연산자를 사용

 


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

캡슐화

  • 한 코드에서 여러가지 기능을 하는 것이 아니라 각각 필요한 기능들을 구현하고 그 기능들을 뭉쳐 새로운 구현을 할 수 있다
  • 정보에 대한 은닉, 외부에 대한 접근 제한이 가능하고 코드의 안정성과 유지보수성 상승

상속

  • 기존 클래스에서 새로운 클래스로 확장
  • 공통된 부분을 상위클래스로 두고 하위클래스에서 각자의 기능을 구현
  • 코드의 중복을 줄이고 구조화와 유지보수 용이하게 함

다형성

  • 동일한 코드에서 다양한 처리들을 가능하게 하는 것 (EX - 메서드 오버로딩)
  • 코드의 가독성과 재사용성 상승

추상화

  • 복잡한 개념을 단순화하고 기능에 집중
  • 세부 구현 내용은 미루고 핵심 개념에 집중

객체

  • 데이터와 메서드를 가지고 동작하는 프로그램의 형태
  • 모듈화, 재사용성 높음

클래스의 구성요소

  1. 필드 : 멤버 변수
  2. 메서드 : 멤버 함수
  3. 생성자 : 객체가 생성될 때 자동으로 호출되는 메서드
  4. 소멸자 : 메모리에서 해제되거나 소멸될 떄 자동으로 호출되는 메서드 
  5.  

클래스

  • 객체를 생성하기 위한 설계도
  • 속성과 동작들이 정의되어있음
  • 클래스를 사용하여 인스턴스 생성
  • 붕어빵 틀로 비유 가능 - 클래스 자체로는 할 수 있는게 별로 없고 클래스로 객체를 생성하여 객체를 사용해야함

객체

  • 클래스의 인스턴스, 붕어빵
  • 클래스로부터 생성되며 독립적인 상태임

클래스 선언과 인스턴스

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

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

구조체와 클래스

  • 클래스는 레퍼런스 타입 - Person p는 변수나 구조체처럼 실제로 공간이 생긴 것이 아님
    new를 사용해 실제 공간을 만들고 그것을 p에 연결함
  • new가 필요없이 바로 선언 가능한 구조체와 달리 new를 반드시 사용해야함
  • 둘 다 사용자 정의 형식을 만들 때 샤용되지만 구조체는 값 형식이고 스택에 자동으로 할당되며 복사될 때 값이 복사된다
  • 반면 클래스는 참조 형식이고 힙에 할당되며 이 작업은 컴퓨터가 자동으로 하는 것이 아닌 개발자가 한다
  • 구조체는 상속이 불가능하지만 클래스는 단일 및 다중 상속이 가능하다
  • 구조체는 작거나 단순한 데이터의 저장에 적합하고 클래스는 더 크고 복잡한 기능 구현에 적합하다

접근 제한자

 

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

클래스, 필드, 메서드 등의 접근 범위를 제한하는 키워드

  • public : 자유롭게 접근 가능
  • private : 클래스안에서만 접근 가능
  • protected : 클래스 내부와 상속받은 클래스에서 접근 가능

필드와 메서드

  • 필드 : 클래스 내부에 선언된 변수. 데이터를 저장
  • 메서드 : 클래스 내부에 선언된 함수. 클래스의 동작을 정의

필드

class Player
{
    // 필드 선언
    private string name;
    private int level;
}
  • 클래스, 구조체 내에 생성된 데이터를 저장하는 변수
  • 객체의 특징이나 속성을 표현하기 위해 사용
  • 보통 private로 외부의 접근을 제한
  • 접근이 필요할 경우 접근 가능한 함수나 프로퍼티를 통해 간접적으로 접근

메서드

class Player
{
    // 필드
    private string name;
    private int level;

    // 메서드
    public void Attack()
    {
        // 공격 동작 구현
    }
}
  • 클래스나 구조체의 기능을 정의
  • 객체의 행동이나 동작을 구현
  • 보통 public으로 외부에서 호출이 가능하도록 만듦
  • 메서드를 호출하기 위해서는 new로 인스턴스를 생성 후 인스턴스를 통해 메서드를 호출  
Player player = new Player();  // Player 클래스의 인스턴스 생성
player.Attack();  // Attack 메서드 호출

생성자와 소멸자

생성자

  • 객체가 생성될 때 호출되는 메서드
  • 객체를 초기화하고 초기값을 설정
  • 클래스와 이름이 같고 반환값은 없음
  • 객체를 생성할 때 new 키워드화 함께 호출
  • 여러개 정의할 수 있고 매개변수의 개수나 타입에 따라 다른 생성자 호출 가능(생성자 오버로딩)
  •  반환값, 매개변수가 없는 디폴트 생성자가 자동으로 생성되지만 사용자가 생성자를 직접 정의한다면 디폴트 생성자는 자동으로 생성되지 않음
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);           // 매개변수를 받는 생성자 호출

소멸자

  • 객체가 소멸될 때 자동으로 호출되는 메서드
  • 클래스와 이름이 같고 이름 앞에 ~을 붙여 구분
  • 반환타입이 없고 매개변수를 가질 수 없음
  • C#에선 가비지 컬렉터가 자동으로 메모리를 해제할 때 소멸자가 자동으로 호출
  • 소멸자는 사용자가 호출하는 것이 아니기 때문에 오버로딩이 없음
class Person
{
    private string name;

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

    ~Person()
    {
        Console.WriteLine("Person 객체 소멸");
    }
}

프로퍼티

  • private 등으로 은닉화 한 필드값을 외부에서 접근할 수 있게 만드는 중간 매개역
  • 객체의 필드에 직접 접근이 아닌 간접 접근으로 접근을 허용하거나 거부하거나 유효한 데이터인지 등을 확인

프로퍼티 구문

[접근 제한자] [데이터 타입] 프로퍼티명
{
    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 프로퍼티에 값 설정
person.Age = 25;        // Age 프로퍼티에 값 설정

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // Name과 Age 프로

 접근 제한자 적용 및 유효성 검사

class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        private set { name = value; } // set은 제한하고 get만 외부에서 사용
    }

    public int Age
    {
        get { return age; }
        set
        {
            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 프로퍼티에 
								접근하여 값을 출력합니다.

자동 프로퍼티

[접근 제한자] [데이터 타입] 프로퍼티명 { get; set; }
  • 따로 구현하지 않아도 자동으로 필드의 역할을 진행
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}");  // 값을 읽어 출력

상속과 다형성

상속

  • 기존 클래스를 확장 또는 재사용하여 자식 클래스를 생성하는 것
  • 자식 클래스는 부모 클래스의 멤버를 상속받아 사용 가능
  • 상속을 통해 부모클래스의 기능 확장 또는 수정하여 새 클래스 정의 가능
  • 코드 재사용, 계층 구조로 코드 표현, 유지 보수성 증가
  • 단일상속 : 하나의 자식 클래스가 하나의 부모 클래스만 상속받음
  • 다중상속 : 하나의 자식 클래스가 여러개의 부모 클래스를 상속받음 C# 지원 안됨
  • 인터페이스 상속: 클래스가 인터페이스를 상속받음 인터페이슨느 다중상속 지원
  • 부모클래스 멤버에 접근: 부모 클래스에 정의된 멤버들을 자식 클래스에서 쉽게 재사용
  • 메서드 재정의: 부모 클래스의 메서드를 그대로 사용할 필요 없이 재정의 하거나 확장하여 사용
  • 상속의 깊이: 다수의 계층의 상속 구조 가능
    상속의 깊이가 깊어지면 클래스 간 관계가 복잡해지므로 적절한 깊이로 맞출 필요가 있음\

접근 제한자와 상속

  • 부모 클래스의 접근 제한자에 따라 자식 클래스가 접근할 수도 못할 수도 있음
// 부모 클래스
public class Animal
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void Eat()
    {
        Console.WriteLine("Animal is eating.");
    }

    public void Sleep()
    {
        Console.WriteLine("Animal is sleeping.");
    }
}

// 자식 클래스
public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine("Dog is bark.");
    }
}

public class Cat : Animal
{
    public void Sleep()
    {
        Console.WriteLine("Cat is sleeping.");
    }

    public void Meow()
    {
        Console.WriteLine("Cat is meow.");
    }
}

// 사용 예시
Dog dog = new Dog();
dog.Name = "Bobby";
dog.Age = 3;

dog.Eat();      // Animal is eating.
dog.Sleep();    // Animal is sleeping.
dog.Bark();     // Dog is barking

Cat cat = new Cat();
cat.Name = "KKami";
cat.Age = 10;

cat.Eat();
cat.Sleep();
cat.Meow();

다형성

같은 타입이지만 다양한 동작을 수행할 수 있는 능력

가상 메서드

  • 부모클래스에서 정의, 자식클래스에서 재정의
  • virtual 키워드로 선언하며 필요할 때 자식클래스에서 재정의
  • 자식클래스에서 부모클래스의 메서드 변경 또는 확장 가능
public class Unit
{
    public virtual void Move()
    {
        Console.WriteLine("두발로 걷기");
    }

    public void Attack()
    {
        Console.WriteLine("Unit 공격");
    }
}

public class Marine : Unit
{

}

public class Zergling : Unit
{
    public override void Move()
    {
        Console.WriteLine("네발로 걷기");
    }
}
// 사용 예시
// #1 참조형태와 실형태가 같을때
Marine marine = new Marine();
marine.Move();
marine.Attack();

Zergling zergling = new Zergling();
zergling.Move();
zergling.Attack();

// #2 참조형태와 실형태가 다를때
List<Unit> list = new List<Unit>();
list.Add(new Marine());
list.Add(new Zergling());

foreach (Unit unit in list)
{
    unit.Move();
}

부모의 형태로 자식클래스 객체를 관리할 때 virtual로 자식 오브젝트에서 재정의되었는지 확인 후 재정의 되었다면 다시 자식 클래스에서 재정의된 메서드를 사용한다

추상 클래스와 메서드

  • 직접 인스턴스를 생성할 수 없음
  • 주로 상속 용도의 베이스로 사용
  • abstract 키워드로 선언, 추상 메서드를 포함할 수 있음
  • 추상 메서드는 구현되지 않은 메서드로 상속받은 자식 클래스에서 구현해야함
abstract class Shape
{
    public abstract void Draw();
}

class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

class Square : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a square");
    }
}

class Triangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a triangle");
    }
}
List<Shape> list = new List<Shape>();
list.Add(new Circle());
list.Add(new Square());
list.Add(new Triangle());

foreach (Shape shape in list )
{
    shape.Draw();
}

오버라이딩과 오버로딩

  • 오버라이딩
    • 부모 클래스에 이미 정의된 메서드를 자식 클래스에서 재정의
    • 메서드 이름,  매개변수 및 반환타입이 똑같아야 함
    • 이를 통해 자식클래스가 부모클래스의 메서드를 재정의하여 자신에게 맞는 동작으로 구현
public class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape.");
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle.");
    }
}

public class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a rectangle.");
    }
}

Shape shape1 = new Circle();
Shape shape2 = new Rectangle();

shape1.Draw();  // Drawing a circle.
shape2.Draw();  // Drawing a rectangle.
  • 오버로딩
    • 이름이 같은 여러개의 메서드를 정의
    • 함수 호출 시 골라서 불러올 수 있게 할 수 있음
public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }
}

Calculator calc = new Calculator();
int result1 = calc.Add(2, 3);         // 5
int result2 = calc.Add(2, 3, 4);      // 9

제너릭


제너릭 사용법

  • 클래스나 메서드를 일반화시켜 다양한 자료형에 대응할 수 있도록 만드는 기능
  • 코드를 하나만 짜놓고 다양한 자료형들을 사용할 수 있게 만듦
  • 코드의 재사용성 향상
  • C#에서는 <T> 키워드로 제너릭 선언
  • 제너릭 클래스나 메서드에서 사용할 자료형은 사용 시점에 결정
  • 사용할 때는 <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(2);
intStack.Push(3);
Console.WriteLine(intStack.Pop()); // 출력 결과: 3
  • 제너릭을 2개 이상 사용할 떄 예시
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();

Pair<double, bool> pair2 = new Pair<double, bool>(3.14, true);
pair2.Display();

출력 결과

First: 1, Second: One
First: 3.14, Second: True

out, ref 키워드

사용법

  • 메서드에서 매개변수를 전달할 때 사용
  • out: 메서드 반환 값을 매개변수로 전달
  • ref: 매개변수를 수정하여 원래 값에 영향을 줄 때 사용
  • 실제 변수에 영향을 주기 때문에 사용에 주의가 필요
// out 키워드 사용 예시
void Divide(int a, int b, out int quotient, out int remainder)
{
    quotient = a / b;
    remainder = a % b;
}

int quotient, remainder;
Divide(7, 3, out quotient, out remainder);
Console.WriteLine($"{quotient}, {remainder}"); // 출력 결과: 2, 1

// 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

주의사항

  • 값의 변경 가능성 : 메서드 내에서 해당 변수의 값을 직접 변경할 수 있으므로 주의가 필요
  • 성능 이슈 : 값의 복사가 없기 때문에 성능상 빨라지지만 남용시 코드의 가독성이 떨어지고 유지보수가 어려워짐
  • 변수 변경 여부 주의 : 매개변수가 메서드 내에서 무조건 변경되어야 하므로 값의 비교 등이 필요할 시 이전 값의 복사나 다른 변수에 저장하는 등의 조치가 필요

'내일배움캠프_TIL' 카테고리의 다른 글

8월 18일 TIL  (0) 2023.08.18
8월17일 TIL  (0) 2023.08.17
8월 14일 TIL  (0) 2023.08.14
8월11일 TIL  (0) 2023.08.11
8월10일 TIL  (0) 2023.08.10