인터페이스, 열거형, 예외처리, 값형과 참조형, 박싱과 언박싱, 델리게이트, 람다, Func과 Action, LINQ, Nullable, 문자열 빌더(StringBuilder)

ggm-_-·2024년 9월 25일
0

TIL (Tody I Learn)

목록 보기
7/27
post-custom-banner

(수)2024.09.25 자 끄적끄적...
00시부터 밤 새서 적은 내용이 포함되어 있다. 원래 자기전꺼를 다 넣으려고 했는데 이전꺼에 넣긴 좀 많아서...
근데 아침에 8시 넘어서 잠든 건 안비밀. 그냥 오늘 쓴 내용이다.
뭔가 중요한 게 좀 많다.

C#에서 다중 상속을 사용하지 않는 이유

다이아몬드 문제

- 다이아가 싫은 다중 상속 ㅠㅠ<이거 없애주세요

  • 한 클래스가 두 개 이상의 부모 클래스로부터 동일한 멤버를 상속받음.
  • 두 부모가 동일한 부모 클래스로부터 상속을 받았다고 했을 때, 어떤 부모 클래스의 멤버를 사용해야 하는지 모호해짐.
    이걸 해결하기 위한 코드가 필요한데, 코드 복잡 및 가독성 저하가 유발됨.

설계의 복잡성 증가

  • 클래스 간의 관계가 복잡해짐
  • 어떤 클래스로부터 어떤 멤버를 상속받을지 결정해야 하게 됨.
  • 상속 관계 파악 난감 및 코드 유지 보수성 저하. 대충 그냥 클래스가 줄이어폰 꼬인 것마냥 보인다는 거 같음.

이름 충돌과 충돌 해결의 어려움

뭐 계속 똑같은 얘기 하네ㅡㅡ< 이것도 없애주세요

  • 여러 부모 클래스로부터 상속받은 멤버들의 이름 충돌 가능성 有
  • 충돌 해결 위해 멤버이름 재정의 등등 뭐 여러가지 문제 있음.
  • 코드 복잡성 증가 및 오류 발생 가능성 증가. 아유 알겠어유

설계의 일관성과 단순성 유지

  • C#은 단일 상속을 통해 설계의 일관성과 단순성 유지
  • 단일 상속을 통해, 클래스 간의 관계 명확 및 코드의 가독성 증가. 음 그냥 다중상속 단점의 반대 속성이네
  • 인터페이스를 통해 다중 상속과 유사한 기능 구현. 호오...

1. 인터페이스 Interface

인터페이스 사용 이유

  • 코드의 재사용성
  • 다중 상속 제공
  • 유연한 설계: 클래스와 인터페이스간의 느슨한 결합 형성 가능. 대충 그냥 언제든지 넣고 뺄 수 있으니 그런느낌인듯?

인터페이스 정의

public interface IMovable
{
    void Move(int x, int y); // 이동 메서드 선언
}
  • 인터페이스 상속하는 구현 클래스 생성
public class Player : IMovable
{
    public void Move(int x, int y)
    {
        // 플레이어의 이동 구현
    }
}

public class Enemy : IMovable
{
    public void Move(int x, int y)
    {
        // 적의 이동 구현
    }
}
  • 인터페이스 사용해서 객체 이동하기
IMovable movableObject1 = new Player();
IMovable movableObject2 = new Enemy();

movableObject1.Move(5, 0); // 플레이어 이동
movableObject2.Move(1, 9); // 적 이동

인터페이스보고 느낀점.

솔직히 말해서 처음엔 추상클래스와의 차이점을 느끼지 못하였다.
그런데 생각해보니 추상클래스는 클래스라 다중상속이 안되니까 정해져 있는 클래스에 계속 넣어놔야 되고 유동적이지 못하다는 생각이 팍 들었다.
만약 추상클래스를 이용해 여려 군대에 똑같은 인터페이스를 만드려고 한다면 추상클래스(나 메서드)가 속한 클래스의 전체를 상속하여야 할 것이다.
근데 그래도 아직 애매한 부분이 있다. 어차피 선언만 하고 들어가서 다시 만들어줘야 하는데 굳이 이렇게 형식을 만들어놓을 필요가 있을까...? 라는?

그런데 다음 예제를 공부하면서 이걸 사용해야만 하는 이유에 대해 알아버렸다.

  • 아이템 사용 구현 예제
// 아이템을 사용할 수 있는 인터페이스
public interface IUsable
{
    void Use();
}

// 아이템 클래스
public class Item : IUsable
{
    public string Name { get; set; }

    public void Use()
    {
        Console.WriteLine("아이템 {0}을 사용했습니다.", Name);
    }
}

// 플레이어 클래스
public class Player
{
    public void UseItem(IUsable item)
    {
        item.Use();
    }
}

// 게임 실행
static void Main()
{
    Player player = new Player();
    Item item = new Item { Name = "Health Potion" };
    player.UseItem(item);
}

이 코드를 처음 보고 든 생각은, 아니, Player클래스의 UseItem의 매개변수로 왜 IUsable 타입을 받는 거지? Item 타입을 받아서 쓰는 게 더 가독성도 좋고 IUseble엔 Use()의 구현부가 선언도 안 되어 있는데 저게 실행이 되긴 하는건가?
라는 거였다.
그런데, 천만의 말씀 만만의 콩떡. 그게 아니다. 이 멍청한 자식아. (뭐에요 나 안 멍청해요...)
메인 함수에서 볼 수 있듯, Player의 인스턴스인 player의 메서드 UseItem으로 받고 있는 인자는 이전예제처럼 인터페이스로 선언한 객체가 아닌 Item클래스의 객체 item.
? 근데 그래서 뭐 어쩌라고? 라고 생각한다면 큰 오산이다.
여기서 눈여겨 볼 점은 인터페이스를 인자로 넘겨줄 수 있다는 점이다.
인터페이스를 상속받은 클래스를 넣어도 인터페이스만 알아서 넘겨준다는 점이다. 이 얼마나 사용성이 편리한가...!
지금은 예제이지만, 만약에 체력 물약 말고 마나 물약, 속도 물약, 힘의 물약, 귀환 주문서 등등 여러 아이템이 생길 것이다.
이런 아이템의 사용 인터페이스인 IUsable이 있다고 했을 때, Player의 사용 함수에서 IUsable인터페이스를 매개변수로 지정함으로서 모든 아이템의 사용을 단 한 줄의 코드로 요약할 수 있다는 것이다!!!!!!!!!!!!!!!!!!!!!!!!!! 이 얼마나....(이하생략)
각설하고 하나의 코드사용으로 수만개의 아이템에 대한 사용함수를 각각 들어가서 쉽게 자원 소모 없이 불러온다는 것은 아주 큰 의미가 있다.
인터페이스의 말 그대로 클래스를 합리적으로 이용하는 UI라는 느낌이다. 너희 너희 각각 다른 클래스들이 가진 공통된 의미의 기능인 요녀석 사용~(@^0^@)/ (하는 함수 작성..ㅇㅇ)
이것은 정말 혁신이다. 와 C#최고! 아니 근데 다른언어도 이 정도면 인터페이스 있겠는데? 예전에 배웠는데 큰 의미를 못느껴서 까먹었나 보다.

아무튼 요 인터페이스는 아주 자주 보게될 것만 같은 느낌이 든다. 개념을 확실히 알아두자.

인터페이스 vs 추상클래스

  • 상속받아서 그냥 쓴다. 딱히 다른 클래스와 같은 형식으로 공유하지 않는다 =>추상클래스 사용
  • 다른 클래스와 같은 형식으로 공유하여 사용할 필요가 있다. 다중상속이 필요하다 => 인터페이스 사용

뭐 그냥 추상클래스는 클래스를 생성할 때, 직접 사용자에게 정의를 맞기고 싶다는 생각이 들 때 사용하는 거 같고,
인터페이스는 여러 클래스에서 상속받는 동일한 형식의 객체가 있을 때 사용하면 된다는 거 같다. 인터페이스는 쉽게 클래스에 넣었다 떼었다 할 수 있고, 같은 인터페이스에 대해서 동일하게 취급하여 다룰 수 있기 때문에 이러한 기능들을 사용할 때 쓰면 유용하다.

2. 열거형 Enums

이것도 공부해보니 매우 자주 쓸 친구이다.(확신)

열거형(Enums) 사용하는 이유

  • 가독성: 연관있는 상수들을 명명하여 코드에서의 가독성을 높인다.(고양이=1, 강아지=2라고 해놨는데 고양이 자체를 1로 쓸 수 있게 된다)
  • 자기 문서화: 의미있는 이름들을 사용한다. 가독성이랑 같은 맥락...이다. 상수의 의미를 알 수 있게 만들어준다는 거다.(뭐 똑같은 말을 다르게 하네; 하나만 쓰던가ㅡㅡ)
  • 스위치 문과의 호환성: 스위치랑 호환 짱짱맨이다.

특징

  • 관련된 상수 집합 정의할 때 사용
  • 각 상수는 정수 값으로 지정됨.(Dictionary랑 비슷한 느낌? Dictionary에서 key가 enum 타입이고 value가 int형인 느낌인가)

다음은 코드로 정의를 보여주겠다.

열거형 정의

enum MyEnum
{
    Value1, //	값 지정을 해주지 않았다면 각각 1
    Value2, //	2
    Value3  //	3 으로 초기화 된다.
}
  • 열거형 사용
MyEnum myEnum = MyEnum.Value1;
  • 열거형 상수 값 지정
enum MyEnum
{
    Value1 = 10,
    Value2,	// 지정이 안 된 열거형은 이전 숫자 + 1로 초기화 된다. 여기선 11
    Value3 = 20
}
  • 열거형 형변환
int intValue = (int)MyEnum.Value1;  // 열거형 값을 정수로 변환
MyEnum enumValue = (MyEnum)intValue;  // 정수를 열거형으로 변환
  • 스위치문과의 연계
switch(enumValue)
{
    case MyEnum.Value1:
        // Value1에 대한 처리
        break;
    case MyEnum.Value2:
        // Value2에 대한 처리
        break;
    case MyEnum.Value3:
        // Value3에 대한 처리
        break;
    default:
        // 기본 처리
        break;
}

열거형을 이용한 가독성을 높이는 코드 예제이다.

  • 코드
// 월 열거형
public enum Month
{
    January = 1,
    February,
    March,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December
}

// 처리하는 함수
static void ProcessMonth(int month)
{
    if (month >= (int)Month.January && month <= (int)Month.December)
    {
        Month selectedMonth = (Month)month;
        Console.WriteLine("선택한 월은 {0}입니다.", selectedMonth);
        // 월에 따른 처리 로직 추가
    }
    else
    {
        Console.WriteLine("올바른 월을 입력해주세요.");
    }
}

// 실행 예제
static void Main()
{
    int userInput = 7; // 사용자 입력 예시
    ProcessMonth(userInput);
}

ProcessMonth의 if문에서 month를 열거형을 이용해 조건문을 작성함으로서 가독성을 높인다.
뭐하는 코드인지 바로바로 알아볼 수 있다.
month가 January부터 December 사이에 오면 if문을 실행시키는 구나~!

다음은 열거형의 게임 사용 사례 코드이다.

  • 코드
// 게임 상태
enum GameState
{
    MainMenu,
    Playing,
    Paused,
    GameOver
}

// 방향
enum Direction
{
    Up,
    Down,
    Left,
    Right
}

// 아이템 등급
enum ItemRarity
{
    Common,
    Uncommon,
    Rare,
    Epic
}

게임 개발에서 숫자 대신에 다음과 같은 열거형을 사용해서 코드를 작성하면 가독성을 심히 높일 수 있다. (음 음)

3. 예외처리

예외?

  • 프로그램 실행 중 예기치 않은 상황
  • 프로그램 실행 방해 및 오류 일으킴(웬만하면)

예외 처리 필요성?

  • 예외 대비->안정성 유지
  • 오류 상황 적절히 처리-> 프로그램 계속 실행 가능하게 (위랑 똑같은 말...;)
  • 디버깅 용이하게 한다. (? 왜요? 프로그램 실행이 안 끊겨서 그런가? 진짜 모름.)

예외 처리 구현

  • C#에서는 try-catch블록을 사용하여 예외처리 수행

  • try블록에서 예외 위험성이 있는 코드를 작성, catch블록에서 예외 처리

  • 코드

try
{
    // 예외가 발생할 수 있는 코드
}
catch (ExceptionType1 ex)
{
    // ExceptionType1에 해당하는 예외 처리
}
catch (ExceptionType2 ex)
{
    // ExceptionType2에 해당하는 예외 처리
}
finally
{
    // 예외 발생 여부와 상관없이 항상 실행되는 코드
}

예외 처리 순서

  • 예외가 일어난 모든 catch블록은 실행된다. 이 때, catch블록은 위에서부터 순서대로 처리한다.
  • 예외 타입이 만약 상속 관계에 있는 경우, 상위 예외 타입의 catch블록을 먼저 실행한다.

다중 catch 블록

  • 여러개의 catch블록으로 다양한 타입의 예외 처리 가능. -예외 타입에 따른 예외 처리 코드 생성

예외 객체

  • 예외 객체를 사용하여 예외에 대한 정보에 접근하여 확인하고 처리할 수 있음. -예외의 타입, 메시지 등을 확인

finally 블록

  • 예외발생 여부와 관계없이 항상 실행되는 코드
  • 예외 처리의 마지막 단계로, 예외 정리 작업이나 리소스 해제 등의 코드 포함 가능
  • try-catch블록 뒤에 작성하고, 생략가능

실행시점

  • 예외가 발생한 경우: 예외 처리 과정 후 실행 (뭐 예외 발생한 catch끝나고 실행되는듯)
  • 예외 발생 x: 그냥 try블록 후에 실행
    (? 근데 순서대로 그냥 실행되는데 이걸 정리까지 해야되나?)

사용자 정의 예외

사용자 정의 예외 클래스

  • 사용자가 직접 자신의 필요에 따라 예외 클래스를 작성가능
  • 사용자 정의 예외 클래스는 Exception 클래스를 상속받아 작성하고, 추가적인 기능이나 정보 제공을 작성하기도

사용자 정의 예외 처리

  • 사용자 정의 예외 발생 시, 클래스 이름으로 선언한 변수로 작성된 catch문으로 해당 예외 처리
  • 예외에 대한 적절한 로직 작성 가능
  • 사용자 정의 예외 처리 사용 예제
public class NegativeNumberException : Exception
{
    //생성자. :base(message) 부분은 부모의 생성자. NegativeNumberException이 실행되면 부모의 생성자가 먼저 실행된다.
    public NegativeNumberException(string message) : base(message)
    {
    }
}

try
{
    int number = -10;
    if (number < 0)
    {
        //throw로 의도적으로 예외 발생
        throw new NegativeNumberException("음수는 처리할 수 없습니다.");
    }
}
catch (NegativeNumberException ex)
{
    Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
    Console.WriteLine("예외가 발생했습니다: " + ex.Message);
}

4. 값형과 참조형

값형(Value Type)

갑형은 존나쌔다. ? 밤 새더니 정신 나갔니?

  • 값형은 변수에 직접 저장한다
  • 변수가 실제 데이터 보유. 다른변수에 할당할 때는 값이 복사됨.
  • 값형 변수의 수정은 다른 변수에 영향x
  • int, float 등 기본 변수 Type들이 값형에 해당.

참조형(Referecne Type)

  • 변수가 데이터에 대한 참조(메모리의 주소)를 저장한다.
  • 변수가 실제 데이터를 가리키는 참조를 갖고 있음. 다른 변수에 할당할 때 참조가 복사됨.
  • 참조형 변수의 수정은 같은 참조를 가진 다른 변수의 데이터에 영향을 줌. (데이터가 저장된 주소가 같아서)
  • 클래스, 배열, 인터페이스 등이 해당

값형과 참조형의 차이점

  • 값형-실제 데이터 저장, 참조형-데이터의 참조 저장.(특정 주소지의 데이터 저장으로 보면 될듯)
  • 값형은 값 복사, 참조형은 참조 복사
  • 값형은 복사 시 독립된 데이터 가지고, 참조형은 동일한 데이터 가짐.

박싱과 언박싱

값형과 참조형 사이의 변환을 나타냄(역시 형님들끼리 친하게 지내시네)

박싱(Boxing)

  • 값형을 참조형으로
  • 메모리의 힙 영역에 할당된 객체로 래핑함(뭐라는 거야)
  • 박싱된 객체와 원래의 값형은 서록 독립적이라 값을 수정해도 상호 영향x

언박싱(UnBoxing)

  • 참조형을 값형으로

박싱과 언박싱의 주요 특징

  • 박싱된 객체는 힙 영역에 할당되서 가비지 컬렉션의 대상이 됨.=> 메모리 마니씀(뭔진 모르겠지만 그렇데)
  • 값형과 참조형 사이의 변환 작업이므로 무분별한 사용은 성능에 영향을 미침(왠진 모르지만 그렇데)

object 클래스

그냥 예제에 쓰여서 설명 들어감

  • object 클래스는 모든 클래스의 직간접적인 최상위 클래스이다.
  • 모든 클래스는 object에 상속되고, object는 모든 형식을 참조할 수 있는 포괄적인 타입임.

예제 코드

  • 박싱과 언박싱
// 박싱과 언박싱
int num1 = 10;
object obj = num1; // 박싱
int num2 = (int)obj; // 언박싱
Console.WriteLine("num1: " + num1); // 출력 결과: 10
Console.WriteLine("num2: " + num2); // 출력 결과: 10
  • 리스트에서의 활용
List<object> myList = new List<object>();

// 박싱: 값 형식을 참조 형식으로 변환하여 리스트에 추가
int intValue = 10;
myList.Add(intValue); // int를 object로 박싱하여 추가

float floatValue = 3.14f;
myList.Add(floatValue); // float를 object로 박싱하여 추가

// 언박싱: 참조 형식을 값 형식으로 변환하여 사용
int value1 = (int)myList[0]; // object를 int로 언박싱
float value2 = (float)myList[1]; // object를 float로 언박싱

5. 델리게이트(Delegate)

  • 델리게이트는 메서드를 참조하는 타입이다.
  • 다른 프로그래밍 언어의 함수 포인터랑 비슷함.
  • 델리게이트를 사용하면 메서드를 매개변수로 전달하거나 변수에 할당할 수 있다.

구현 코드

delegate void MyDelegate(string message);

static void Method1(string message)
{
    Console.WriteLine("Method1: " + message);
}

static void Method2(string message)
{
    Console.WriteLine("Method2: " + message);
}

class Program
{
    static void Main()
    {
        // 델리게이트 인스턴스 생성 및 메서드 등록
        MyDelegate myDelegate = Method1;
        myDelegate += Method2;

        // 델리게이트 호출
        myDelegate("Hello!");

        Console.ReadKey();
    }
}
  • 출력
Method1: Hello!
Method2: Hello!

델리게이트로 선언한 것과 똑같은 반환Type과 똑같은 매개변수를 가지고 있는 Method1과 Method2를 만든다.
메인함수에서 델리게이트 타입으로 변수를 선언하고 델리게이트와 같은 모양의 함수(메서드)를 가진 함수를 등록한다.
+=을 사용해 메서드를 추가할 수 있다. 이렇게 되면 Method1이후 Method2가 실행된다.

  • 예제 코드
// 델리게이트 선언
public delegate void EnemyAttackHandler(float damage);

// 적 클래스
public class Enemy
{
    // 공격 이벤트
    public event EnemyAttackHandler OnAttack;

    // 적의 공격 메서드
    public void Attack(float damage)
    {
        // 이벤트 호출
        OnAttack?.Invoke(damage);
				// null 조건부 연산자
				// null 참조가 아닌 경우에만 멤버에 접근하거나 메서드를 호출
    }
}

// 플레이어 클래스
public class Player
{
    // 플레이어가 받은 데미지 처리 메서드
    public void HandleDamage(float damage)
    {
        // 플레이어의 체력 감소 등의 처리 로직
        Console.WriteLine("플레이어가 {0}의 데미지를 입었습니다.", damage);
    }
}

// 게임 실행
static void Main()
{
    // 적 객체 생성
    Enemy enemy = new Enemy();

    // 플레이어 객체 생성
    Player player = new Player();

    // 플레이어의 데미지 처리 메서드를 적의 공격 이벤트에 추가
    enemy.OnAttack += player.HandleDamage;

    // 적의 공격
    enemy.Attack(10.0f);
}
  • 출력
플레이어가 10.0의 데미지를 입었습니다.

event

event 는 할당연산자(=)를 사용할 수 없고 +=과 -=을 이용해 조절해야 하며,
클래스 외부에서는 직접이벤트를 호출할 수 없다.(뭥말?)

아무튼, Enemy class에서 event형 델리게이트 OnAttack을 만들었는데, Attack함수에서 OnAttack이 널이 아닌 경우에만 Invoke(damage)를 실행한다. (OnAttack? >>> OnAttack이 null이 아니라면의 뜻인듯)
근데 아직까진 OnAttack은 비어있고 이제 메인함수에서 어떻게 OnAttack에 뭐가 들어가서 작동하는지 살펴보자.
메인함수에서 Enemy와 Player의 객체를 만들어준다.
Enemy의 객체 enemy에 enemy.OnAttack에 += player.HandleDamage를 하여 델리게이트 enemy.OnAttack이 player.HandleDamage메서드를 참조하게 됐다.
이제, enemy.Attack(10.0f)로 인해 OnAttack?.Invoke(damage)가 호출되고,
OnAttack이 null이 아니니 damage값인 10.0f가 OnAttack에 매개변수로 넘겨진다.
마지막으로, 델리게이트에 등록된 player.HandleDamage에 damage값인 10.0f가 인자로 전달되어,
최종적으로 "플레이어가 10.0의 데미지를 입었습니다."가 출력된다.

6. 람다(lambda)

  • 델리게이트에 넣어서 사용하기도
  • 이름없는 함수식(메서드)

람다 구현

  • 형식
    (parameter_list)가 함수의 매개변수 부분, expression부분이 함수의 구현 부분이다.
(parameter_list) => expression
  • 정의하기
    delegate 변수에 함수가 들어가는데 이름없는 함수식인 람다를 바로 넣어줄 수 있다.
//Calculate는 delegate타입
Calculate calc = (x, y) => 
{	
		return x + y;
};

//구현부가 한 줄이면 함수 구현부의 {}생략가능
Calculate calc = (x, y) => x + y;
  • 사용 예제
    람다식을 이용해 delegate 변수에 동작 메서드를 바로 넣어준다.
// 델리게이트 선언
public delegate void GameEvent();

// 이벤트 매니저 클래스
public class EventManager
{
    // 게임 시작 이벤트
    public event GameEvent OnGameStart;

    // 게임 종료 이벤트
    public event GameEvent OnGameEnd;

    // 게임 실행
    public void RunGame()
    {
        // 게임 시작 이벤트 호출
        OnGameStart?.Invoke();

        // 게임 실행 로직

        // 게임 종료 이벤트 호출
        OnGameEnd?.Invoke();
    }
}

// 게임 메시지 클래스
public class GameMessage
{
    public void ShowMessage(string message)
    {
        Console.WriteLine(message);
    }
}

// 게임 실행
static void Main()
{
    // 이벤트 매니저 객체 생성
    EventManager eventManager = new EventManager();

    // 게임 메시지 객체 생성
    GameMessage gameMessage = new GameMessage();

    // 게임 시작 이벤트에 람다 식으로 메시지 출력 동작 등록
    eventManager.OnGameStart += () => gameMessage.ShowMessage("게임이 시작됩니다.");

    // 게임 종료 이벤트에 람다 식으로 메시지 출력 동작 등록
    eventManager.OnGameEnd += () => gameMessage.ShowMessage("게임이 종료됩니다.");

    // 게임 실행
    eventManager.RunGame();
}

7. Func과 Action

델리게이트를 대체하는 미리 정의된 제네릭 형식

Func

  • Func은 값을 반환하는 메서드(를 나타내는 델리게이트)
  • Func<int, string> 있으면 int는 매개변수, string은 반환값이 된다. (int ~(string); 형식의 메서드들을 연결할 수 있다는 뜻)

Action

  • Action은 값을 반환하지 않는 메서드(를 나타내는 델리게이트)

  • Action<int, string> 있으면 int와 string 둘 다 매개변수다. (반환값이 없으니까 void ~(int, string); 형식의 메서드 연결 가능)

  • Func 사용 예제

// Func를 사용하여 두 개의 정수를 더하는 메서드
int Add(int x, int y)
{
    return x + y;
}

// Func를 이용한 메서드 호출
Func<int, int, int> addFunc = Add;
int result = addFunc(3, 5);
Console.WriteLine("결과: " + result);
  • Action 사용 예제
// Action을 사용하여 문자열을 출력하는 메서드
void PrintMessage(string message)
{
    Console.WriteLine(message);
}

// Action을 이용한 메서드 호출
Action<string> printAction = PrintMessage;
printAction("Hello, World!");

미리 정의된 형식을 이용하여 델리게이트를 좀 더 간편하게 쓸 수 있다.
Func과 Action은 그냥 메서드들을 저장하는 형식인 델리게이트를 미리 지정된 형식과 타입으로 쉽게 쓸 수 있게 만든 것이다.
그니까 얘네들도 그냥 메서드 저장하는 거다. 델리쨩처럼. 예? 델리게이트처럼. 델리키 생각나네

8. LINQ

  • .NET 프레임워크에서 제공하는 쿼리언어의 확장
  • 코드에서 쿼리를 던질 수 있다. 누구한테? 컬렉션이나 배열이나 데이터베이스나 xml문서나 여러 데이터 구조들한테
    (쿼리문을 안던진다면 반복문을 다 돌면서 다 모으고 있어야 한다)
  • 데이터베이스의 쿼리와 유사한 방식으로 사용. 당연히 필터링, 정렬, 그룹화, 조인 등 다양한 작업 수행가능
    (그리고 쿼리를 던지고 필터링을 하다 보니까 데이터가 커지고, 쿼리해야할 조건들이 많아진다면 성능에 영향이 많이감)

근데 쿼리가 뭔데요 ㅠㅠ 그런거 몰라요. 기억안나...

쿼리(Query)

쿼리란? 데이터베이스에 정보를 검색하기 위해 요청하는 것.
영어로 '질의' 라는 뜻을 가지고 있는데, 데이터베이스에게 내가 원하는 것을 요구하기 위한 것이다.

LINQ 구조

var result = from 변수 in 데이터소스
             [where 조건식]
             [orderby 정렬식 [, 정렬식...]]
             [select];
  • var는 변수의 자료형을 자동으로 맞춘다.
  • from절에서 데이터소스를 지정(질문을 던질 데이터집합)
  • where절은 선택적으로 사용. 조건식을 지정하여 원하는 데이터를 필터링.
  • orderby절은 선택적으로 사용. 정렬 방식을 지정.
  • select절은 선택적으로 사용. 가져올 데이터를 지정.
  • 예제 코드
// 데이터 소스 정의 (컬렉션)
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

// 쿼리 작성 (선언적인 구문)
var evenNumbers = from num in numbers
                  where num % 2 == 0
                  select num;

// 쿼리 실행 및 결과 처리
foreach (var num in evenNumbers)
{
    Console.WriteLine(num);
}

9. Nullable형

null이란?

  • '아무것도 없다'를 의미.
  • 참조형 변수에서는 참조가 없다.

Nullable이란?

  • null을 가질 수 없는 값형이 null 값을 갖게 해주는 형식
  • 변수 선언시 타입 뒤에 ?연산자를 사용하여 할당 가능. int? => Nullable

관련 제어 구문

  • .Value: 제어를 할 때, 그냥 변수처럼 접근할 수도 있고, .Value를 사용해서 접근할 수도 있음.
  • .HasValue: .HasValue를 이용해 Value값을 가지고 있는지 검사. 없으면 null반환
  • [nullable변수] ?? 0: nullable형식 뒤에 ??를 사용했을 때, nullable형식이 null이면 ?? 뒤의 0값을 반환함.
    0말고 다른것도 될 것 같긴 한데 확실한 건 모르겠음.
  • 사용 예제
// Nullable 형식 변수 선언
int? nullableInt = null;
double? nullableDouble = 3.14;
bool? nullableBool = true;

// 값 할당 및 접근
nullableInt = 10;
int intValue = nullableInt.Value;

// null 값 검사
if (nullableDouble.HasValue)
{
    Console.WriteLine("nullableDouble 값: " + nullableDouble.Value);
}
else
{
    Console.WriteLine("nullableDouble은 null입니다.");
}

// null 병합 연산자 사용
// nullableInt ?? 0과 같이 사용되며, nullableInt가 null이면 0을 반환합니다.
int nonNullableInt = nullableInt ?? 0;
Console.WriteLine("nonNullableInt 값: " + nonNullableInt);

10. 문자열 빌더(StringBuilder)

문자열 빌더란?

  • 문자열들을 내부적인 버퍼에 넣어놓고 교환만 해놨다가 필요할 때 문자열로 만들어주는 기능이다.>>가변성
  • 때문에 Append(), Insert(), Replace(), Remove()등 다양한 메서드를 제공하여 문자열을 조작할 수 있다.>>문자열 조작
  • 내부 버퍼를 사용하여 문자열을 조작하므로 반복적인 문자열 작업 발생해도 메모리 할당 및 해제 오버헤드가 감소한다.(뭐라는 건지 모르겠지만 아무튼 좋다는 거 아녀) >>효율적인 메모리 관리

문자열 빌더(StringBuilder) 쓰는 이유?
"A" + "B" + "C"를 사용하면 "A" + "B" 에서 "A", "B", "A" + "B"의 결과인 "AB"가 메모리에 저장을 해 놓는다. 또 "AB" + "C" 에서 "AB", "C", "ABC"를 저장하게 된다. 그러면 "A" + "B" + "C"를 하기 위해서, "A", "B", "C", "AB", "ABC"가 메모리에 저장되기 때문에 많은양의 메모리를 잡아먹는다.
그렇기 때문에 문자열 빌더나 String포멧을 사용하는 것을 추천한다.

  • 코드 예제
StringBuilder sb = new StringBuilder();

// 문자열 추가
sb.Append("Hello");
sb.Append(" ");
sb.Append("World");

// 문자열 삽입
sb.Insert(5, ", ");

// 문자열 치환
sb.Replace("World", "C#");

// 문자열 삭제
sb.Remove(5, 2);

// 완성된 문자열 출력
string result = sb.ToString();
Console.WriteLine(result);
  • 출력
HelloC#
profile
미숙한 초보 게임 개발자
post-custom-banner

0개의 댓글