티스토리 뷰

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 (23제곱)
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;
                }

            }

        }
    }
}

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
글 보관함