Nullable 형

null은 아무것도 없다, 즉 참조를 하지 않았다는 뜻이다.

값형은 원래 null을 가질 수 없다. 그러나 값형에 Nullable을 달면 null이라고 하는 값을 사용할 수 있게 된다. 주로 값형 변수가 null인지 아닌지를 구분해야 하는 경우에 사용된다.

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

 

 

문자열 빌더(StringBuilder)
  • 문자열 조작

문자열들을 내부적인 버퍼에 넣어 놓고 받아서 조합만 해놨다가 필요할 때 문자열로 만들어준다.

Append(), Insert(), Replace(), Remove() 등 다양한 메서드를 치환할 수 있다.

  • 가변성

내부 버퍼를 사용하여 크기가 동적으로 변할 수 있다. 추가했다 뺐다 바꾸기도 가능하며 추가적인 메모리 할당을 해서 가변적으로 길이를 조절할 수 있다.

  • 효율적인 메모리 관리

주요 메서드

Append: 뒤에 문자열 추가

Insert: 지정한 위치에 문자열 삽입

Remove: 지정한 위치에서 문자열 제거

Replace: 문자열의 일부를 다른 문자열로 대체

Clear: StringBuilder의 내용을 모두 지운다.

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

 

 

델리게이트(Delegate)

메서드를 참조하는 타입. 메서드를 변수에 저장할 수 있다.

 

델리게이트 구현

event는 할당(=)을 사용할 수 없고 +=나 -=만 사용할 수 있으며, 클래스 외부에서 직접 이벤트를 호출할 수 없다. 이를 통해 해당 코드의 보안성을 높이고 캡슐화 한다.

// 델리게이트 선언
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);
}

 

 

람다(Lambda)

이름이 없는 익명 메서드 중 하나. 참조를 통해 컨트롤 한다.

// 델리게이트 선언
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();
}

 

 

Func과 Action

Func과 Action은 미리 정의된 제네릭 형식이다.

Func은 반환값이 있는 델리게이트이고 Action은 반환값이 없이 실행만 되는 메서드이다. Func에 int와 string을 준다면 int는 매개변수이고 뒤의 마지막으로 나오는 string은 반환값이다. Action 같은 경우에는 매개변수가 있으면 반환값이 없기 때문에 둘다 매개변수이다.

// 게임 캐릭터 클래스
class GameCharacter
{
    private Action<float> healthChangedCallback;

    private float health;

    public float Health
    {
        get { return health; }
        set
        {
            health = value;
            healthChangedCallback?.Invoke(health);
        }
    }

    public void SetHealthChangedCallback(Action<float> callback)
    {
        healthChangedCallback = callback;
    }
}

// 게임 캐릭터 생성 및 상태 변경 감지
GameCharacter character = new GameCharacter();
character.SetHealthChangedCallback(health =>
{
    if (health <= 0)
    {
        Console.WriteLine("캐릭터 사망!"); // 코드가 한 줄일 경우 중괄호 생략 가능
    }
});

// 캐릭터의 체력 변경
character.Health = 0;

델리게이트와 동일하지만 이미 정의, 구현 되어 있기 때문에 안전하고 코드가 한결 간단해진다.

 

 

LINQ(Language Intergrated Query)

.NET 프레임워크에서 제공되는 쿼리 언어 확장. 코드에서 collection이나 배열이나 데이터베이스에 쿼리를 던질 수 있다.

 

구조

var result = from 변수 in 데이터소스
             [where 조건식]
             [orderby 정렬식 [, 정렬식...]]
             [select 식];
  • var : LINQ에서 쿼리를 전달받는 형식들이 다양할 수 있기 때문에 사용한다.
  • from : 변수를 꺼내온다.
  • where절
  • orderbt 정렬
  • 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);
}

 

 

 예외 처리

예외 처리는 프로그램이 갑작스럽게 종료 되거나 멈추는 상황을 예방하고 안정적으로 유지할 수 있도록 구현해준다.

 

예외 처리 구현

try에 예외가 발생할 수 있는 코드를 작성하고 catch에 예외 처리를 한다.

catch문은 위에서부터 순서대로 실행된다. 다중으로 붙일 수 있어 여러 가지 예외 처리를 할 수 있고, 예외 객체를 받아올 수 있어 예외 처리에 발생한 여러 정보들을 표시할 수 있다.

마지막으로 finally은 예외와 상관 없이 무조건 실행되는 코드이다. 필요하지 않다면 생략 가능하다.

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

 

사용자 정의 예외

사용자가 필요에 따라 자신만의 예외 클래스를 작성할 수 있다.

Exception 클래스를 상속받아 작성한다.

public class NegativeNumberException : Exception
{
    public NegativeNumberException(string message) : base(message)
    {
    }
}
internal class Program
    {
    static void Main(string[] args)
    {
        try
        {
            int number = -10;
            if (number < 0)
            {
                throw new NegativeNumberException("음수는 처리할 수 없습니다.");
            }
        }
        catch (NegativeNumberException ex)
        {
            Console.WriteLine(ex.Message);
        }
        catch (Exception ex)
        {
            Console.WriteLine("예외가 발생했습니다: " + ex.Message);
        }
    }
}

 

예외 처리 할 때는 가능한 구체적인 예외 클래스를 사용하는 것이 코드가 안정적이고 예외 처리에 대한 처리가 더욱 정확해진다.

if else문으로 예외를 처리하면 되지 않나 생각이 들었었는데 이 때문에 예외 처리를 사용한다는 것을 깨달았다.

 

 

값형과 참조형

값형(Value Type)

변수에 값을 직접 저장한다. 변수가 독립적으로 데이터를 가지며 어딘가에 할당하거나 대입 해야할 때 값을 복사해서 준다.

예) int, float, double, bool 등

struct MyStruct
{
    public int Value;
}

MyStruct struct1 = new MyStruct();
struct1.Value = 10;

MyStruct struct2 = struct1; // struct2는 struct1의 값 복사

struct2.Value = 20;

Console.WriteLine(struct1.Value); // 출력 결과: 10

 

 

참조형(Reference Type)

변수에 데이터에 대한 참조(메모리 주소)를 저장한다. 변수가 동일한 데이터를 참조하여 할당하거나 대입 해야할 때 참조를 복사해서 준다.

예) 클래스, 배열, 인터페이스 등

class MyClass
{
    public int Value;
}

MyClass obj1 = new MyClass();
obj1.Value = 10;

MyClass obj2 = obj1; // obj2는 obj1과 동일한 객체를 참조

obj2.Value = 20;

Console.WriteLine(obj1.Value); // 출력 결과: 20

 

 

박싱과 언박싱

박싱은 값형을 참조형으로 변환한다. 언박싱은 박싱으로 참조형이었던 값형을 다시 값형으로 돌려놓는다.

 

object 형식은 직,간접적으로 모든 클래스의 최상위 클래스이다.

// 값형
int x = 10;
int y = x;
y = 20;
Console.WriteLine("x: " + x); // 출력 결과: 10
Console.WriteLine("y: " + y); // 출력 결과: 20

// 참조형
int[] arr1 = new int[] { 1, 2, 3 };
int[] arr2 = arr1;
arr2[0] = 4;
Console.WriteLine("arr1[0]: " + arr1[0]); // 출력 결과: 4
Console.WriteLine("arr2[0]: " + arr2[0]); // 출력 결과: 4

// 박싱과 언박싱
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로 언박싱

값형을 참조형으로 변환할 때, 값형이 사라지는 것이 아니라 남아있고 값형에 대한 참조형으로 변환되어 새롭게 받아지는 것이다. 그렇기 때문에 너무 많은 박싱과 언박싱이 일어나면 성능이 저하된다.

 

값과 참조형, 박싱과 언박싱은 자주 사용되기 때문에 잘 알아두는 것이 좋을 것 같다.

TextRPG 개인과제를 팀과제로 구현하기로 했다.

아직 개인학습이 끝나지 않은 분들도 계셔서 간단한 사항만 결정하고 금요일에 게임 컨셉을 정하고 역할 분담을 하기로 했다.

 

<회의 사항>

스크럼을 오전, 오후 2회로 늘리고 개인학습으로 대화를 많이 하지 않아서 친해지는 시간을 갖기로 했다.

맥 유저와 윈도우 유저 확인하고 발표와 영상 등을 정했다. 깃을 써보지 않은 분들이 많아서 목요일 저녁에 한 번씩 커밋 연습해보는 시간을 갖는 것이 좋을 것 같다.

 

게임 방향성은 팀원분이 추천해주신 참고 할 만한 게임인 다크드래곤을 찾아보고, TRPG 형식으로 주사위를 굴려 공격하는 형식은 어떨까 생각 중이다.

+ Recent posts