[240103]TIL

응징·2024년 1월 3일
0

TIL

목록 보기
9/36
post-thumbnail

오늘 한일

<영상 시청>
4주차 강의 완강

<Text게임 선택 요구사항 구현>
아이템 추가 -나만의 아이템
아이템 판매 (판매가격 85%)
장착 개선
던전입장 기능 추가
휴식기능 추가
레벨업 추가

강의

인터페이스

장점
코드의 재 사용성
다중 상속 적용
코드의 유연성

인터페이스 특징
인터페이스란 클래스가 구현해야 하는 멤버들을 정의하는 것

// 인터페이스 1
public interface IItemPickable
{
    void PickUp();
}

// 인터페이스 2
public interface IDroppable
{
    void Drop();
}

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

    public void PickUp()
    {
        Console.WriteLine("아이템 {0}을 주웠습니다.", Name);
    }

    public void Drop()
    {
        Console.WriteLine("아이템 {0}을 버렸습니다.", Name);
    }
}

// 플레이어 클래스
public class Player
{
    public void InteractWithItem(IItemPickable item)
    {
        item.PickUp();
    }

    public void DropItem(IDroppable item)
    {
        item.Drop();
    }
}

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

    // 아이템 주울 수 있음
    player.InteractWithItem(item);

    // 아이템 버릴 수 있음
    player.DropItem(item);
}

인터페이스 vs 추상클래스

인터페이스는 추상적인 동작만 정의하는 반면에 추상 클래스는 일부 동작의 구현을 가지며, 추상 메서드를 포함한다.

단일 상속일 경우에도 간단하게 정의만을 명시할 때 사용한다. 물론 자식 클래스에서도 많이 쓰이는 기능은 부모 추상 클래스에서 구현하고 내려오는 것이 좋다.

그리고 무엇보다 다중 상속면에서는 정의만하고 여러가지 인터페이스에서 상속을 받을 수 있다.

열거형 (Enums)

값 지정

enum MyEnum
{
    Value1 = 10,
    Value2,
    Value3 = 20
}

상수값만 받음

열거형 형변환

int intValue = (int)MyEnum.Value1; // 열거형 값을 정수로 변환
MyEnum enumValue = (MyEnum)intValue; // 정수를 열거형으로 변환

위는 값을 받은 '10' 변환, 아래는 'Value1 ' 로 변환

enum DaysOfWeek
{
    Sunday,
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}

class Program
{
    static void Main(string[] args)
    {
        DaysOfWeek day = DaysOfWeek.Monday;
        Console.WriteLine("Today is " + day);
    }
}

상수 값을 저장하는 관련된 데이터를 저장 할때 좋을듯

너무 많은 예외를 예상하기 보다 우려가 되는 부분을 예외처리한다.

값형 / 참조형 /박싱언박싱

using System;

class Program
{
    static void Main()
    {
        // 값형
        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
    }
}

값형은 각 데이터에 저장
참조형은 한 주소에서 값을 받아옴 (같은 주소에서 받아오는 변수가 또 있다면 결과는 두 변수가 같다)
박싱과 언박싱은 다른 자료형에 값을 주는 걸 말하는 건가? 그리고 다시 돌려 받을땐 언박싱이라고 하는 듯

박싱 언박싱은 너무 많이 처리하면 성능에 영향을 끼친다. 너무 신경쓸 필요는 없되, 너무 신경끄면 안좋다.

예외처리

예외는 프로그램 실행 중에 발생하는 예기치 않은 상황을 처리하는 로직

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

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

델리게이트

delegate int Calculate(int x, int y);

static int Add(int x, int y)
{
return x + y;
}

class Program
{
static void Main()
{
// 메서드 등록
Calculate calc = Add;

    // 델리게이트 사용
    int result = calc(3, 5);
    Console.WriteLine("결과: " + result);
}

}

델리게이트는 메서드를 참조하는 타입이다. 보통 일상에서 쓰이기 보단 함수에 접근이 어려울때 델리게이트로 접근하는 경우에 사용한다.

class Program
    {
        delegate void MyDelegate(string message);

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

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

        static void Main(string[] args)
        {

            // 델리게이트 인스턴스 생성 및 메서드 등록
            MyDelegate myDelegate = Method1;
            myDelegate += Method2;

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

            Console.ReadKey();

        }
       

    }

메소드를 += 해서 여러개의 메소드를 연결 할수 있다. 아래는 결과 값이다.

Func과 Action

델리게이트를 대체 한다

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

Func<int, int, int>는 파라미터 정의

// Action을 사용하여 문자열을 출력하는 메서드
void PrintMessage(string message)
{
    Console.WriteLine(message);
}

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

메소드를 받아드리지만 반환값은 없음.

이름?.함수()

OnAttack?.Invoke(damage);

여기서 ?는 null이면 실행하지 말고 null이 아닐경우 실행하라는 뜻

람다

람다(lambda)는 익명 메서드를 만드는 방법으로 이름 없이 메서드를 사용할수 있다.

형식

(parameter_list) => expression

정의

Calculate calc = (x, y) => 
{	
		return x + y;
};

Calculate calc = (x, y) => x + y;

그 자리에서 함수를 만드는건가? 정의는 굳이 필요없는 메소드이다.

메인에서 빠르게 함수를 만들 수도 있고 그것을 델리게이트와 응용 할수도 있다.
다만 델리게이트랑 응용하는 모습만 봐서 실질적으로 어디에 쓰이는지는 잘 모르겠다.

LINQ

데이터베이스 쿼리와 유사한 방식으로 데이터를 필터링, 정렬, 그룹화, 조인 등 다양한 작업을 수행한다.

다만 그만큼 데이터를 많이 쓰기 때문에 성능에 영향을 미친다. 적당히 사용하기

var result = from 변수 in 데이터소스
             [where 조건식]
             [orderby 정렬식 [, 정렬식...]]
             [select 식];
  • from 절에서는 데이터 소스를 지정
  • where 절은 선택적으로 사용하며, 조건식을 지정하여 데이터를 필터링
  • select 절은 선택적으로 사용하며, 조회할 데이터를 지정

그외: var, orderby

 static void Main()
        {
            // 데이터 소스 정의 (컬렉션)
            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);
            }
        }

알아서 필터링하니 반복문 중에서도 foreach문과 사용하기 좋을 듯하다. 굳이 if문으로 예외를 두지 않아도 될테니.

Nullable 형

Nullable은 C#에서 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);

null 여부 검사

nullableDouble.HasValue

if(nullableDouble.HasValue)
{
	//값이 있음
}
else
{
	//null 일경우
}

null 병합 연산자 사용

nullableInt ?? 0

// nullableInt ?? 0과 같이 사용되며, nullableInt가 null이면 0을 반환
int nonNullableInt = nullableInt ?? 0;

문자열 빌더 (StringBuilder)

문자열에 대한 추가, 삽입, 치환, 삭제 작업을 수행할 수 있다

StringBuilder sb = new StringBuilder();

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

// 문자열 삽입
sb.Insert(5, ", ");         // 띄어쓰기는 6번째로 가게됨

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

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

// 완성된 문자열 출력
string result = sb.ToString();
Console.WriteLine(result);

TextGame 개인과제

foreach문 예외 오류

foreach (var item in Player.haveitem1) //이름을 찾아 장착, 가진것 해제
                {
                    if (Foritem[userInput - 1].Name == item.Name)
                    {


                        if (item.Setting) //장착이 되있었다면
                        {
                            item.Setting = false;
                        }

                        Player.haveitem1.Remove(item);
                        Player.PlusDefense += item.Plusstat;
                    }
                }

위와 같이 같은 이름을 찾아 if문을 만족할 경우 해당 item을 삭제하려고 했으나, 아래의 오류가 떴다.

System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'

foreach문으로 열거하고 있는 도중 컬렉션을 수정하려고 시도할 때 발생하는 문제이다.이럴때 해당 컬렉션을 삭제하는 방법으로는 역순으로 루프를 실행하고, 삭제 작업은 for문 가장 마지막 줄에서 실행했다.

for (int ii = Player.haveitem1.Count - 1; ii >= 0; ii--)
                {
                    var item = Player.haveitem1[ii];
                    if (Foritem[userInput - 1].Name == item.Name)
                    {
                        if (item.Setting)
                        {
                            item.Setting = false;
                        }
                        item.Have = false;
                        SailConsole();
                        Player.PlusDefense += item.Plusstat;
                        Player.haveitem1.RemoveAt(ii); // 인덱스 ii의 아이템을 삭제
                    }
                }

애초에 item 클래스를 생성했으니 item을 삭제하는 메소드도 해당 클래스에서 생성할 걸 그랬다. 다음엔 클래스를 데이터 저장소처럼 쓰지 말고 적극적으로 메소드도 생성해야겠다.

if문으로 예외처리 경험

int userInput = int.Parse(Console.ReadLine());

            if (userInput == 0)
            {
                Console.Clear();
                Store();

            }
            else if (Foritem[userInput - 1] == null)//잘못된 숫자
            {
                Fail();
                Purchase();
            }

else if (Foritem[userInput - 1] == null)에서 지정해준 숫자 외의 것들을 처리하려 했으나, 아무래도 초기화 되지 않은 list에 null이 들어가진 않는 모양이다

else if (userInput <= 0 || userInput > Foritem.Count)

사용자가 입력한 값이 리스트의 Foritem.Count 보다 큰 숫자거나 작을경우 처리하게끔 했다.

애초에 if문은 try-catch과 달리 예외를 감지하고 처리하는 것이 떨어진다. 해당 구조가 if문과 밀접하게 관련이 있어 if문으로 우선 처리했으나 try-catch에 익숙해져 이런 사항을 예견할수 있도록 하자

rend.NextDouble()

랜덤으로 소수점의 실수형을 주고 싶었으나 rend.Next는 정수만 가능한듯 하다. 따라서 rend.NextDouble() 메소드를 사용

rend.NextDouble는 rend.Next랑 좀 매커니즘이 달라서 주의가 필요하다.

double min = 0.1;
double max = 0.2;
double randomInRange = random.NextDouble() * (max - min) + min; // min 이상 max 미만의 랜덤한 값

기억

람다함수 - 구문은 외우겠는데 어떨때 보통 쓰는지 감이 잘 안잡힘 (따로 공부)
nullableInt ?? 0 - null을 넣을 수도 있는 변수
try-catch - 자꾸 if문으로 더럽게 하지말고 공부하기
JSON 파일 저장하기 할때 가장 많이 쓰는 파일인듯(내일)

내일은 JSON 파일로 캐릭터 클래스 , 아이템 클래스 리스트 , 그 외의 변수(MAP,UntilLevelUP)들을 불러오고 저장하는 법을 배울것

여담

한번 함수가 점점 만들어지면 비슷한 로직을 수행하기 때문에 초반 틀을 잡는 것만 넘으면
후반에는 쉬워진다. 함수 뿐만 아니라 배열(리스트), 클래스의 초반 기반을 잘 잡아나야 후반에 편해진다. 반대로 신경을 덜 쓰면 후반에 고생하니까 로직을 잘 구상해보자.

profile
Unity 개발 위주로 정리합니다

0개의 댓글