<영상 시청>
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 추상클래스
인터페이스는 추상적인 동작만 정의하는 반면에 추상 클래스는 일부 동작의 구현을 가지며, 추상 메서드를 포함한다.
단일 상속일 경우에도 간단하게 정의만을 명시할 때 사용한다. 물론 자식 클래스에서도 많이 쓰이는 기능은 부모 추상 클래스에서 구현하고 내려오는 것이 좋다.
그리고 무엇보다 다중 상속면에서는 정의만하고 여러가지 인터페이스에서 상속을 받을 수 있다.
값 지정
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를 사용하여 두 개의 정수를 더하는 메서드
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;
그 자리에서 함수를 만드는건가? 정의는 굳이 필요없는 메소드이다.
메인에서 빠르게 함수를 만들 수도 있고 그것을 델리게이트와 응용 할수도 있다.
다만 델리게이트랑 응용하는 모습만 봐서 실질적으로 어디에 쓰이는지는 잘 모르겠다.
데이터베이스 쿼리와 유사한 방식으로 데이터를 필터링, 정렬, 그룹화, 조인 등 다양한 작업을 수행한다.
다만 그만큼 데이터를 많이 쓰기 때문에 성능에 영향을 미친다. 적당히 사용하기
var result = from 변수 in 데이터소스
[where 조건식]
[orderby 정렬식 [, 정렬식...]]
[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은 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);
nullableDouble.HasValue
if(nullableDouble.HasValue)
{
//값이 있음
}
else
{
//null 일경우
}
nullableInt ?? 0
// nullableInt ?? 0과 같이 사용되며, nullableInt가 null이면 0을 반환
int nonNullableInt = nullableInt ?? 0;
문자열에 대한 추가, 삽입, 치환, 삭제 작업을 수행할 수 있다
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);
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을 삭제하는 메소드도 해당 클래스에서 생성할 걸 그랬다. 다음엔 클래스를 데이터 저장소처럼 쓰지 말고 적극적으로 메소드도 생성해야겠다.
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.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)들을 불러오고 저장하는 법을 배울것
한번 함수가 점점 만들어지면 비슷한 로직을 수행하기 때문에 초반 틀을 잡는 것만 넘으면
후반에는 쉬워진다. 함수 뿐만 아니라 배열(리스트), 클래스의 초반 기반을 잘 잡아나야 후반에 편해진다. 반대로 신경을 덜 쓰면 후반에 고생하니까 로직을 잘 구상해보자.