티스토리 뷰
C#으로 함수의 좀 고급?버전과 namespace, Enum, Struct, Class를 배웠다
그리고 과제는 인벤토리와 슈팅게임(미완성)을 구조체와 함수로 리팩토링 하는 거였는데 유용했던 것 같다
💻 C#
함수
오버로딩
// 오버로딩
static int Multiply(int a, int b)
{
return a * b;
}
static double Multiply(double a, double b)
{
return a * b;
}
out 함수
int q, r; // out 함수 호출 전에 초기화 할 필요가 없다
Divide(10, 3, out q, out r);
Console.WriteLine($" 몫: {q}, 나머지 : {r}");
static void Divide(int a, int b, out int quotient, out int remainder)
{
quotient = a / b;
remainder = a % b;
}
여러 개의 값을 반환할 때 사용한다
ref 함수
int value = 5; // 함수 호출 전 초기화 해야 함
Increase(ref value);
Console.WriteLine(value);
static void Increase(ref int num)
{
num += 10;
}
기존 변수의 값을 변경할 때 사용한다
✅ out vs ref 차이점
키워드 | out | ref |
초기화 여부 | 함수 내부에서 반드시 값을 할당해야 함 | 함수 호출 전에 반드시 초기화해야 함 |
주 사용 목적 | 여러 개의 값을 반환할 때 | 기존 변수의 값을 변경할 때 |
값 유지 여부 | 함수 호출 전 값 무시 | 함수 호출 전 값 유지 |
void Example(out int x) { x = 10; } // x는 반드시 함수 내부에서 값 할당
void Example(ref int x) { x += 10; } // x는 호출 전 초기화 필요
params 함수
Console.WriteLine(Sum(1, 2, 3));
Console.WriteLine(Sum(1, 2));
static int Sum(params int[] nums)
{
int total = 0;
foreach(int num in nums)
{
total += num;
}
return total;
}
매개변수 개수를 미리 정하지 않고 함수에 전달할 때 사용한다
재귀함수
Console.WriteLine(Factorial(5));
static int Factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * Factorial(n - 1);
}
}
Factorial(5)부터 시작해서 작은 값으로 호출하지만, 마지막 호출부터 다시 돌아오면서 연산(반환)이 수행된다
람다함수
// (매개변수) => 표현식
// (매개변수) => { 문장들 }
// 기본식 (int를 입력받아 int를 반환)
Func<int, int> square = x => x * x;
Console.WriteLine(square(5)); // 25
// 매개변수 여러개
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(3, 7)); // 10
// 반환값이 없음
Action<string> greet = name => Console.WriteLine($"Hello, {name}!");
greet("Alice"); // Hello, Alice!
// 여러 줄로 이루어짐
Func<int, int, int> multiply = (x, y) =>
{
int result = x * y;
Console.WriteLine($"곱셈 결과: {result}");
return result;
};
Console.WriteLine(multiply(4, 5));
int[] numbers = { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (var num in evenNumbers)
{
Console.WriteLine(num); // 2, 4
}
람다는 LINQ에서 데이터를 조작할 때 자주 사용된다
namespace
namespace MyNamespace
{
class MyClass
{
public void SayHello()
{
Console.WriteLine("Hello from MyNamespace!");
}
}
}
using MyNamespace; // 네임스페이스 가져오기
class Program
{
static void Main()
{
MyClass myClass = new MyClass(); // MyNamespace.MyClass 사용
myClass .SayHello();
}
}
namespace 는 클래스, 인터페이스, 구조체, 이넘 등을 그룹화 하는데 사용된다. 이름 충돌을 방지하고 코드 관리를 쉽게 하기 위해 사용된다
이름 충돌 방지에 사용
namespace A
{
class MyClass
{
public void SayHello() => Console.WriteLine("Hello from A!");
}
}
namespace B
{
class MyClass
{
public void SayHello() => Console.WriteLine("Hello from B!");
}
}
class Program
{
static void Main()
{
A.MyClass obj1 = new A.MyClass();
B.MyClass obj2 = new B.MyClass();
obj1.SayHello(); // Hello from A!
obj2.SayHello(); // Hello from B!
}
}
Enum
enum DayOfWeek
{
Sunday, // 0
Monday, // 1
Tuesday, // 2
Wednesday, // 3
Thursday, // 4
Friday, // 5
Saturday // 6
}
enum HttpStatus
{
OK = 200,
Created = 201,
Accepted = 202,
NotFound = 404
}
var today = DayOfWeek.Thursday;
Console.WriteLine($"{today} ({(int)today})"); // Thursday (4)
var status = HttpStatus.NotFound;
Console.WriteLine($"{status} ({(int)status})"); // NotFound (404)
enum Weapontype
{
Sword,
Bow,
Staff
}
var weapon = Weapontype.Bow;
PrintWeapon(weapon);
static void PrintWeapon(Weapontype weapon)
{
switch (weapon)
{
case Weapontype.Sword:
Console.WriteLine("검을 선택했습니다");
break;
case Weapontype.Bow:
Console.WriteLine("활을 선택했습니다");
break;
case Weapontype.Staff:
Console.WriteLine("지팡이를 선택했습니다");
break;
}
}
enum은 switch와 함께 자주 쓰인다
Struct
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public void Print()
{
Console.WriteLine($"Porint({X}, {Y})");
}
}
var p1 = new Point(10, 20);
var p2 = p1;
p2.X = 50;
p1.Print(); // Point(10, 20)
p2.Print(); // Point(50, 20)
struct Rectangle
{
public int Width;
public int Height;
public int GetArea() => Width * Height;
}
var rect = new Rectangle();
rect.Width = 10;
rect.Height = 20;
Console.WriteLine($"Rectangle Area : {rect.GetArea()}"); // Rectangle Area : 200
구조체는 값이 복사되어 p1과 p2는 다른 개체다.
Class
class Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
public void Print()
{
Console.WriteLine($"Point({X}, {Y})");
}
}
var p1 = new Point(10, 20);
var p2 = p1;
p2.X = 50;
p1.Print(); // Point(50, 20)
p2.Print(); // Point(50, 20)
클래스는 참조가 복사되어 p1과 p2는 같은 객체를 참조한다
✅ Struct vs Class 차이점
항목 구조체 (struct) 클래스 (class)
메모리 할당 | 값 형식 (Value Type) - **스택(Stack)**에 저장 | 참조 형식 (Reference Type) - **힙(Heap)**에 저장 |
기본 동작 | 값을 직접 복사 (깊은 복사) | 참조를 전달 (얕은 복사) |
상속 | 상속 불가능 | 상속 가능 |
기본 생성자 | 기본 생성자 직접 정의 불가 | 기본 생성자 명시적으로 정의 가 |
사용 목적 | 작은 크기의 데이터 그룹, 성능이 중요한 경우 | 복잡한 데이터 구조, 상속이 필요한 경우 |
초기화 필요 여부 | 모든 필드가 반드시 초기화되어야 함 | 필드를 초기화하지 않아도 됨 (기본값을 가짐) |
✅ C# 접근제한자
접근제한자 설명 적용 범위
public | 모든 코드에서 접근 가능 | 모든 클래스, 구조체, 메서드, 변수 |
private | 해당 클래스 또는 구조체 내에서만 접근 가능 | 해당 클래스 또는 구조체 내 |
protected | 해당 클래스 및 해당 클래스를 상속받은 클래스에서 접근 가능 | 해당 클래스 및 그 하위 클래스 |
internal | 같은 어셈블리 내에서만 접근 가능 | 같은 어셈블리 내의 모든 클래스 및 구조체 |
protected internal | 같은 어셈블리 내의 모든 클래스에서 접근 가능, 또는 상속받은 클래스에서 접근 가능 | 같은 어셈블리 내, 또는 해당 클래스를 상속받은 클래스 |
어셈블리는 namespace 에 따라 구분된다
class의 기본접근제한자는 internal 이고 struct는 private 이다
public struct Student
{
public string Name { get; private set; }
public int KoreanScore { get; private set; }
public int EnglishScore { get; private set; }
public int MathScore { get; private set; }
public Student(string name, int koreanScore, int englishScore, int mathScore)
{
Name = name;
KoreanScore = koreanScore;
EnglishScore = englishScore;
MathScore = mathScore;
}
public void Print()
{
Console.WriteLine($"{Name}\\t{KoreanScore}\\t{EnglishScore}\\t{MathScore}");
}
}
이렇게 get, set 에 서로 다른 접근제한자를 이용해 캡슐화를 할 수 있다
🕹️ 콘솔 게임 만들기
인벤토리
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Inventory
{
class Program
{
const int MAX_ITEMS = 10;
static string[] itemNames = new string[MAX_ITEMS];
static int[] itemCounts = new int[MAX_ITEMS];
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
var sword = "🗡️ 칼";
var potion = "🧪 포션";
var shield = "🛡️ 방패";
AddItem(potion, 5);
AddItem(sword, 1);
AddItem(potion, 3);
ShowInventory();
Console.WriteLine("- 포션 2개 사용!");
RemoveItem(potion, 2);
ShowInventory();
Console.WriteLine("- 방패 1개 제거 시도");
RemoveItem(shield, 1);
ShowInventory();
Console.WriteLine("- 포션 6개 사용(초과 사용 테스트)");
RemoveItem(potion, 7);
ShowInventory();
}
static void AddItem(string name, int count)
{
for(int i=0; i<MAX_ITEMS; i++)
{
if (itemNames[i] == name) // 이미 있는 아이템이면 개수 증가
{
itemCounts[i] += count;
return;
}
}
for(int i=0; i<MAX_ITEMS; i++)
{
if (itemNames[i] == null) // 빈 슬롯에 새로운 아이템 추가
{
itemNames[i] = name;
itemCounts[i] = count;
return;
}
}
Console.WriteLine("인벤토리가 가득 찼습니다.");
}
static void RemoveItem(string name, int count)
{
for(int i=0; i<MAX_ITEMS; i++)
{
if (itemNames[i] == name) // 해당 아이템 찾기
{
if (itemCounts[i] >= count) // 개수가 충분하면 차감
{
itemCounts[i] -= count;
if (itemCounts[i] == 0) // 개수가 0이면 삭제
{
itemNames[i] = null;
}
return;
}
else
{
Console.WriteLine("아이템 개수가 부족합니다!");
return;
}
}
}
Console.WriteLine("아이템을 찾을 수 없습니다!");
}
static void ShowInventory()
{
Console.WriteLine("========== 현재 인벤토리 ==========");
bool isEmpty = true;
for(int i=0; i<MAX_ITEMS; i++)
{
if (itemNames[i] != null)
{
Console.WriteLine($"{itemNames[i]} (x{itemCounts[i]})");
isEmpty = false;
}
}
Console.WriteLine("===================================");
if (isEmpty)
{
Console.WriteLine("인벤토리가 비어있습니다.");
}
Console.WriteLine();
}
}
}
슈팅게임 (미완성)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ShootingGame
{
class Program
{
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.SetWindowSize(80, 25);
Console.SetBufferSize(80, 25);
Console.CursorVisible = false;
var keyInfo = new ConsoleKeyInfo();
//int x = 10, y = 10;
int playerX = 0;
int playerY = 12;
var player = new string[]
{
"->",
">>>",
"->"
}; // 배열 문자열로 그리기
var stopwatch = new Stopwatch(); // 루프 활용
stopwatch.Start();
long prevSecond = stopwatch.ElapsedMilliseconds;
while (true)
{
long currentSecond = stopwatch.ElapsedMilliseconds;
if(currentSecond - prevSecond >= 500)
{
//Console.WriteLine("루프");
Console.Clear();
keyInfo = Console.ReadKey(true); // 키 입력 받기
switch (keyInfo.Key)
{
case ConsoleKey.UpArrow: if (playerY > 0) playerY--; break;
case ConsoleKey.DownArrow: if (playerY < Console.WindowHeight - 3) playerY++; break;
case ConsoleKey.LeftArrow: if (playerX > 0) playerX--; break;
case ConsoleKey.RightArrow: if (playerX < Console.WindowWidth - 3) playerX++; break;
case ConsoleKey.Spacebar: Console.SetCursorPosition(playerX + 3, playerY + 1); Console.Write("🔹"); break;
case ConsoleKey.Escape: return;
}
for (int i = 0; i < player.Length; i++) // 플레이어 그리기
{
Console.SetCursorPosition(playerX, playerY + i);
Console.WriteLine(player[i]);
}
prevSecond = currentSecond; // 이전 시간 업데이트
}
}
}
}
}
💫 Tip!
[C#] Math 클래스
Console.WriteLine(Math.PI); // 3.14159265358979
Console.WriteLine(Math.Abs(-10)); // 10 (절댓값)
Console.WriteLine(Math.Max(5, 8)); // 8 (최댓값)
Console.WriteLine(Math.Min(5, 8)); // 5 (최솟값)
Console.WriteLine(Math.Pow(2, 3)); // 8 (2의 3제곱)
Console.WriteLine(Math.Sqrt(25)); // 5 (제곱근)
Console.WriteLine(Math.Round(3.6)); // 4 (반올림)
Console.WriteLine(Math.Ceiling(3.2)); // 4 (올림)
Console.WriteLine(Math.Floor(3.8)); // 3 (내림)
Console.WriteLine(Math.Truncate(3.9)); // 3 (소수점 제거)
Console.WriteLine(Math.Sin(Math.PI / 2)); // 1 (사인 함수)
📝 과제
인벤토리 → 구조체, 함수 사용해서 리팩토링
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Inventory
{
struct Inventory
{
public int MexItemCount { get; set; }
public Item[] Items { get; set; }
public Inventory(int maxItemCount)
{
MexItemCount = maxItemCount;
Items = new Item[maxItemCount];
}
public void AddItem(string name, int count)
{
for (int i = 0; i < MexItemCount; i++)
{
if (Items[i].Name == name)
{
Items[i].Count += count;
return;
}
}
for (int i = 0; i < MexItemCount; i++)
{
if (Items[i].Name == null)
{
Items[i].Name = name;
Items[i].Count = count;
return;
}
}
Console.WriteLine("인벤토리가 가득 찼습니다.");
}
public void RemoveItem(string name, int count)
{
for (int i = 0; i < MexItemCount; i++)
{
if (Items[i].Name == name)
{
if (Items[i].Count >= count)
{
Items[i].Count -= count;
if (Items[i].Count == 0)
{
Items[i].Name = null;
}
return;
}
else
{
Console.WriteLine("아이템 개수가 부족합니다!");
return;
}
}
}
Console.WriteLine("아이템을 찾을 수 없습니다!");
}
public void ShowInventory()
{
Console.WriteLine("┌───────────현재 인벤토리───────────┐");
bool isEmpty = true;
for (int i = 0; i < MexItemCount; i++)
{
if (Items[i].Name != null)
{
Console.WriteLine($" {Items[i].Name} (x{Items[i].Count})");
isEmpty = false;
}
}
Console.WriteLine("└───────────────────────────────────┘");
if (isEmpty)
{
Console.WriteLine("인벤토리가 비어있습니다.");
}
Console.WriteLine();
}
}
struct Item
{
public string Name { get; set; }
public int Count { get; set; }
}
class Program
{
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
var sword = "🗡️ 칼";
var potion = "🧪 포션";
var shield = "🛡️ 방패";
var inventory = new Inventory(10);
inventory.AddItem(potion, 5);
inventory.AddItem(sword, 1);
inventory.AddItem(potion, 3);
inventory.ShowInventory();
Console.WriteLine("- 포션 2개 사용!");
inventory.RemoveItem(potion, 2);
inventory.ShowInventory();
Console.WriteLine("- 방패 1개 제거 시도");
inventory.RemoveItem(shield, 1);
inventory.ShowInventory();
Console.WriteLine("- 포션 6개 사용(초과 사용 테스트)");
inventory.RemoveItem(potion, 7);
inventory.ShowInventory();
}
}
}
슈팅게임 (미완성) → 구조체, 함수 사용해서 리팩토링
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ShootingGame
{
struct Player
{
public int X { get; set; }
public int Y { get; set; }
public string[] Shape { get; set; }
public Player(int x, int y)
{
X = x;
Y = y;
Shape = new string[] { "->", ">>>", "->" };
}
public void HandleKeyInfo(ConsoleKeyInfo keyInfo)
{
switch (keyInfo.Key)
{
case ConsoleKey.UpArrow: if (Y > 0) Y--; break;
case ConsoleKey.DownArrow: if (Y < Console.WindowHeight - Shape.Length) Y++; break;
case ConsoleKey.LeftArrow: if (X > 0) X--; break;
case ConsoleKey.RightArrow: if (X < Console.WindowWidth - 3) X++; break;
case ConsoleKey.Spacebar: Console.SetCursorPosition(X + 3, Y + 1); Console.Write("🔹"); break;
}
}
public void Draw()
{
for (int i = 0; i < Shape.Length; i++)
{
Console.SetCursorPosition(X, Y + i);
Console.WriteLine(Shape[i]);
}
}
}
class Program
{
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
Console.SetWindowSize(80, 25);
Console.SetBufferSize(80, 25);
Console.CursorVisible = false;
var keyInfo = new ConsoleKeyInfo();
var player = new Player(0, 12);
var stopwatch = new Stopwatch();
stopwatch.Start();
long prevSecond = stopwatch.ElapsedMilliseconds;
while (true)
{
long currentSecond = stopwatch.ElapsedMilliseconds;
if(currentSecond - prevSecond >= 100)
{
keyInfo = Console.ReadKey(true);
Console.Clear();
player.HandleKeyInfo(keyInfo);
player.Draw();
prevSecond = currentSecond;
}
}
}
}
}
'Unity > 멋쟁이사자처럼' 카테고리의 다른 글
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 9일차 회고 (0) | 2025.03.07 |
---|---|
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 8일차 회고 (0) | 2025.03.07 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 7일차 회고 (0) | 2025.03.07 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 5일차 회고 (0) | 2025.03.07 |
멋쟁이 사자처럼 부트캠프 유니티 게임 개발 4기 4일차 회고 (0) | 2025.03.07 |