no image
C# - 고급 문법 정복하기 (제너릭, ref/out)
🛠️ C# 고급 문법 정복하기 (제너릭, ref/out)📦 제너릭(Generic) - 타입에 유연한 코드 만들기제너릭이란?클래스나 메서드가 다양한 자료형을 처리할 수 있도록 해주는 기능 형태로 사용하며, 코드 재사용성과 타입 안정성을 높여줌제너릭 클래스 예시class Stack{ private T[] elements = new T[100]; private int top = 0; public void Push(T item) => elements[top++] = item; public T Pop() => elements[--top];} Stack intStack = new Stack();intStack.Push(1);intStack.Push(2);Console.WriteLine(in..
2025.07.07
C#
no image
C# - 객체지향 프로그래밍(OOP) + 클래스와 객체 + 상속과 다형성
🧱 클래스와 객체의 핵심 개념 정리📌 객체지향 프로그래밍(OOP)의 4가지 핵심 원칙캡슐화(Encapsulation)관련된 데이터와 기능을 하나의 단위로 묶고 외부로부터 숨긴다. 필드와 메서드를 클래스 내부에 감춰 안정성과 유지보수성을 높인다.상속(Inheritance)기존 클래스의 속성과 기능을 물려받아 새로운 클래스를 만든다. 코드의 재사용성을 높이고 계층적인 설계를 가능하게 한다.다형성(Polymorphism)같은 이름의 메서드가 다양한 방식으로 동작할 수 있도록 한다. 오버라이딩과 오버로딩을 통해 유연한 코드 구현이 가능하다.추상화(Abstraction)불필요한 세부 사항은 숨기고 핵심 기능만 노출한다. 사용자 입장에서 필요한 정보만 제공해 코드의 간결성과 명확성을 높인다.🧩 클래스와 객체클..
2025.07.07
C#
no image
C# - 틱택토 게임
using System;class Program{ static char[] board = { '1', '2', '3', '4', '5', '6', '7', '8', '9' }; static int turn = 1; // 홀수: X, 짝수: O static bool gameEnded = false; static void Main(string[] args) { while (!gameEnded) { Console.Clear(); DisplayBoard(); PlayerTurn(); CheckWin(); } Console.WriteLine("게임이 종료되었습..
2025.07.07
C#
no image
C# - 메서드와 구조체 완전 정복: 재사용성부터 구조 설계 + 구조체 메모리
🧩 메서드란 무엇인가?✅ 메서드의 정의메서드(Method)는 프로그램에서 특정한 작업을 수행하도록 정의된 코드 블록이다.복잡한 코드를 기능별로 분리하여 모듈화함으로써 코드의 재사용성, 가독성, 유지보수성을 높일 수 있다.C#에서 메서드는 클래스나 구조체 내에 정의되며, 외부에서 호출할 수 있다. 메서드는 일반적으로 다음과 같은 역할을 수행한다.✨ 메서드의 주요 특징코드 재사용성반복적으로 사용되는 코드를 메서드로 정의해 한 번만 작성하면 여러 곳에서 호출하여 사용할 수 있다.모듈화(Modularity)프로그램을 논리적인 단위로 나눌 수 있다. 각 메서드는 하나의 작업에만 집중하도록 설계하는 것이 좋다.가독성 향상메서드 이름만 보고 어떤 동작을 수행하는지 예측 가능하므로 코드 전체의 흐름을 이해하기 쉬워진..
2025.07.07
C#
no image
C# - 배열과 컬렉션 정리 - 1차원부터 다차원, 리스트부터 딕셔너리까지 + 예제
📘 배열과 컬렉션배열1차원 배열동일한 데이터 유형을 연속적으로 저장하며, 인덱스를 통해 요소에 접근할 수 있다. 배열은 선언 시 고정된 크기를 가지며, 크기 이상의 데이터는 저장할 수 없다.int[] array1 = new int[5];array1[0] = 1;...... 예제: 배열을 활용한 합계 계산int[] itemPrices = { 100, 200, 300, 400, 500 };int totalPrice = 0;for (int i = 0; i 배열 실습 예시게임 캐릭터 능력치 배열학생 성적 평균 계산배열 기반 숫자 맞추기 게임다차원 배열행과 열로 구성된 2차원 이상의 배열 구조로, 표 형태의 데이터를 처리할 때 사용된다.int[,] array2D = new int[2, 3];int[,,] arr..
2025.07.07
C#
no image
C# - 조건문과 반복문 - 조건문과 반복문 완전 정복 - 기초 개념부터 실습까지 + 삼항 연산자
📘 조건문과 반복문 1 - if, else if, 중첩 조건문, switch, 삼항 연산자🔍 조건문 개요조건문은 주어진 조건식의 결과에 따라 프로그램의 제어 흐름을 변경하는 제어문이다.if 문if 문은 조건식이 참일 경우에만 해당 블록을 실행한다.if (조건식){ // 조건식이 참일 경우 실행할 코드}한 줄일 경우 중괄호 생략 가능:if (조건식) Console.WriteLine("실행");예시:int playerScore = 80;if (playerScore >= 70) { Console.WriteLine("플레이어의 점수는 70점 이상입니다. 합격입니다!");}Console.WriteLine("프로그램이 종료됩니다.");else 문if 문의 조건이 거짓일 경우 실행할 코드를 지정할..
2025.07.07
C#
no image
C# - 사용자로부터 입력 받기, 계산기 만들기, 온도 변환기 만들기, BMI 계산기 만들기
사용자로부터 입력 받기///이름, 나이 - 입력, 출력Console.Write("이름 입력 : ");string name = Console.ReadLine();Console.Write("나이 입력 : ");int age = int.Parse(Console.ReadLine());Console.WriteLine($"이름 : {name}, 나이 : {age}"); 간단한 사칙연산 계산기 만들기Console.WriteLine("간단한 사칙연산 계산기 만들기");Console.Write("첫 번째 숫자 입력 : ");int num1 = int.Parse(Console.ReadLine());Console.Write("두 번째 숫자 입력 : ");int num2 = int.Parse(Console.ReadLine()..
2025.07.07
C#
no image
C# - 형변환과 입력, 연산자 그리고 문자열 활용
📘 형변환과 입력, 연산자 그리고 문자열 활용🔄 형변환의 개념C#은 형식이 엄격한 언어이기 때문에 서로 다른 자료형 간의 대입이나 연산을 수행할 때 형변환이 필요하다.형변환에는 명시적 형변환과 암시적 형변환 두 가지 방식이 있다.암시적 형변환 (Implicit Casting)작은 범위의 자료형에서 큰 범위의 자료형으로 자동으로 변환된다.int num = 10;float result = num; // int → float 자동 변환명시적 형변환 (Explicit Casting)데이터 손실 우려가 있을 경우, 개발자가 직접 캐스팅을 명시해야 한다.float pi = 3.14f;int intPi = (int)pi; // 소수점 이하 버려짐⌨️ Console 입력 받기C#에서는 Console.ReadLine..
2025.07.07
C#
반응형

🛠️ C# 고급 문법 정복하기 (제너릭, ref/out)


📦 제너릭(Generic) - 타입에 유연한 코드 만들기

제너릭이란?

  • 클래스나 메서드가 다양한 자료형을 처리할 수 있도록 해주는 기능
  • <T> 형태로 사용하며, 코드 재사용성과 타입 안정성을 높여줌

제너릭 클래스 예시

class Stack<T>
{
    private T[] elements = new T[100];
    private int top = 0;

    public void Push(T item) => elements[top++] = item;
    public T Pop() => elements[--top];
}
 
Stack<int> intStack = new Stack<int>();
intStack.Push(1);
intStack.Push(2);
Console.WriteLine(intStack.Pop());  // 출력: 2

제너릭 다중 타입 예시

class Pair<T1, T2>
{
    public T1 First { get; set; }
    public T2 Second { get; set; }

    public Pair(T1 first, T2 second)
    {
        First = first;
        Second = second;
    }

    public void Display()
    {
        Console.WriteLine($"First: {First}, Second: {Second}");
    }
}

Pair<int, string> pair = new Pair<int, string>(1, "One");
pair.Display();  // 출력: First: 1, Second: One

🔄 ref / out 키워드 - 값 전달 방식 제어하기

ref 키워드

  • 변수의 주소를 전달하여, 메서드 내부에서 값 변경 가능
  • 사용 전과 후 모두 초기화되어 있어야 함
void Swap(ref int a, ref int b)
{
    int temp = a;
    a = b;
    b = temp;
}

int x = 1, y = 2;
Swap(ref x, ref y);
Console.WriteLine($"{x}, {y}");  // 출력: 2, 1

out 키워드

  • 메서드에서 값을 반환하는 역할로 사용
  • 메서드 내부에서 반드시 값을 할당해야 함
void Divide(int a, int b, out int quotient, out int remainder)
{
    quotient = a / b;
    remainder = a % b;
}

int q, r;
Divide(10, 3, out q, out r);
Console.WriteLine($"{q}, {r}");  // 출력: 3, 1

⚠️ 주의 사항 정리


구분 ref out
값 변경 여부 기존 값 유지 새 값만 설정됨
호출 전 초기화 필요함 필요 없음
메서드 내 할당 선택적 필수
주 용도 원래 값 변경 다중 반환 값 전달
반응형
반응형

🧱 클래스와 객체의 핵심 개념 정리

📌 객체지향 프로그래밍(OOP)의 4가지 핵심 원칙

  • 캡슐화(Encapsulation)
    관련된 데이터와 기능을 하나의 단위로 묶고 외부로부터 숨긴다. 필드와 메서드를 클래스 내부에 감춰 안정성과 유지보수성을 높인다.
  • 상속(Inheritance)
    기존 클래스의 속성과 기능을 물려받아 새로운 클래스를 만든다. 코드의 재사용성을 높이고 계층적인 설계를 가능하게 한다.
  • 다형성(Polymorphism)
    같은 이름의 메서드가 다양한 방식으로 동작할 수 있도록 한다. 오버라이딩과 오버로딩을 통해 유연한 코드 구현이 가능하다.
  • 추상화(Abstraction)
    불필요한 세부 사항은 숨기고 핵심 기능만 노출한다. 사용자 입장에서 필요한 정보만 제공해 코드의 간결성과 명확성을 높인다.

🧩 클래스와 객체

  • 클래스(Class)
    객체를 만들기 위한 설계도이다. 필드(속성)와 메서드(행동)로 구성된다. 예: 붕어빵 틀
  • 객체(Object)
    클래스에 기반해 만들어진 실체이다. 각 객체는 독립적인 상태를 가진다. 예: 붕어빵

🛠️ 클래스의 구성 요소

  • 필드 (Field): 객체의 데이터를 저장하는 변수
  • 메서드 (Method): 객체가 수행하는 동작
  • 생성자 (Constructor): 객체를 초기화할 때 실행되는 특수 메서드
  • 소멸자 (Destructor): 객체가 소멸될 때 호출되는 특수 메서드

🧱 클래스 선언과 인스턴스 생성

class Person
{
    public string Name;
    public int Age;

    public void PrintInfo()
    {
        Console.WriteLine("Name: " + Name);
        Console.WriteLine("Age: " + Age);
    }
}

// 인스턴스 생성
Person p = new Person();
p.Name = "John";
p.Age = 30;
p.PrintInfo();

🧾 클래스 vs 구조체

항목 클래스 구조체
타입 참조형(Reference Type) 값형(Value Type)
저장 위치 힙(Heap) 스택(Stack)
상속 지원함 지원하지 않음
생성 방식 new 키워드 필요 new 없이도 사용 가능
용도 복잡한 데이터 구조 간단한 데이터 저장
 

🔒 접근 제한자

  • public: 어디서든 접근 가능
  • private: 클래스 내부에서만 접근 가능
  • protected: 클래스와 그 자식 클래스에서만 접근 가능

🧭 프로퍼티(Property)

필드에 대한 접근을 제어하고 유효성 검사를 추가할 수 있는 메커니즘.

 

    1. 프로퍼티란?
    • 프로퍼티는 클래스 멤버로서, 객체의 필드 값을 읽거나 설정하는데 사용되는 접근자(Accessor) 메서드의 조합입니다.
    • 객체의 필드에 직접 접근하지 않고, 간접적으로 값을 설정하거나 읽을 수 있도록 합니다.
    • 필드에 대한 접근 제어와 데이터 유효성 검사 등을 수행할 수 있습니다.
    • 프로퍼티는 필드와 마찬가지로 객체의 상태를 나타내는 데이터 역할을 하지만, 외부에서 접근할 때 추가적인 로직을 수행할 수 있습니다.
    1. 프로퍼티 구문
    • 프로퍼티는 get과 set 접근자를 사용하여 값을 읽고 설정하는 동작을 정의합니다.
    • get 접근자는 프로퍼티의 값을 반환하고, set 접근자는 프로퍼티의 값을 설정합니다.
    • 필요에 따라 get 또는 set 접근자 중 하나를 생략하여 읽기 전용 또는 쓰기 전용 프로퍼티를 정의할 수 있습니다.
[접근 제한자] [데이터 타입] 프로퍼티명
{
    get
    {
        // 필드를 반환하거나 다른 로직 수행
    }
    set
    {
        // 필드에 값을 설정하거나 다른 로직 수행
    }
}

 

  • 프로퍼티 사용 예시
  • 다음은 Player 클래스의 이름과 레벨을 나타내는 name과 level 필드를 캡슐화한 프로퍼티의 예시.
class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set { age = value; }
    }
}
Person person = new Person();
person.Name = "John";   // Name 프로퍼티에 값 설정
person.Age = 25;        // Age 프로퍼티에 값 설정

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // Name과 Age

 

프로퍼티 접근 제한자 적용 & 유효성 검사 예제

class Person
{
    private string name;
    private int age;

    public string Name
    {
        get { return name; }
        private set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set
        {
            if (value >= 0)
                age = value;
        }
    }
}
Person person = new Person();
person.Name = "John";     // 컴파일 오류: Name 프로퍼티의 set 접근자는 private입니다.
person.Age = -10;         // 유효성 검사에 의해 나이 값이 설정되지 않습니다.

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // Name과 Age 프로퍼티에 접근하여 값을 출력합니다.

 

  • 자동 프로퍼티 (Auto Property)
    • 자동 프로퍼티는 프로퍼티를 간단하게 정의하고 사용할 수 있는 편리한 기능입니다.
    • 필드의 선언과 접근자 메서드의 구현을 컴파일러가 자동으로 처리하여 개발자가 간단한 구문으로 프로퍼티를 정의할 수 있습니다.
[접근 제한자] [데이터 타입] 프로퍼티명 { get; set; }

 

아래는 자동 프로퍼티를 사용한 예시.

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}
Person person = new Person();
person.Name = "John";     // 값을 설정
person.Age = 25;          // 값을 설정

Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");  // 값을 읽어 출력

 

Tip

  • 클래스와 객체를 사용하여 프로그램을 작성할 때, 객체 지향 프로그래밍(OOP)의 원칙을 지키도록 노력해야 합니다. 캡슐화, 상속관계, 다형성 등 OOP 개념을 이해하고 활용하여 유지보수성이 높고 재사용 가능한 코드를 작성할 수 있습니다.
  • Properties를 사용하여 필드 접근을 제어하면, 코드의 안정성과 가독성을 높일 수 있습니다.
  • 클래스의 접근 제한자를 적절히 사용하여 필요한 부분만 외부에서 접근 가능하도록 설정하는 것이 좋습니다.

💡 생성자와 소멸자

  • 생성자: 객체 생성 시 자동 실행. 초기화 작업 수행.
    오버로딩을 통해 다양한 방식으로 객체 초기화 가능.
  • 소멸자: 객체가 소멸될 때 자동 호출. 리소스 해제 등의 작업 수행.

 

🧬 상속과 다형성의 핵심 이해

🧾 상속(Inheritance)

  • 정의
    기존 클래스(부모 클래스)의 속성과 기능을 새로운 클래스(자식 클래스)가 물려받는 개념이다.
  • 장점
    • 코드 재사용성 향상
    • 계층적 구조 형성
    • 유지보수 용이
  • 특징
    • C#은 단일 상속만 지원
    • 인터페이스를 통해 다중 상속 효과 구현 가능
    • 부모 클래스의 public 및 protected 멤버는 자식 클래스에서 접근 가능
    • 메서드는 재정의(Override)하여 새로운 동작 구현 가능
  • 기본 예시
class Animal
{
    public void Eat() => Console.WriteLine("먹는다");
}

class Dog : Animal
{
    public void Bark() => Console.WriteLine("짖는다");
}

Dog dog = new Dog();
dog.Eat();  // 상속된 메서드
dog.Bark(); // 자식 클래스 고유 메서드

🌀 다형성(Polymorphism)

  • 정의
    동일한 인터페이스를 통해 여러 클래스가 다양한 방식으로 동작하도록 만드는 객체지향 원칙이다.
  • 종류
    • 오버로딩(Overloading): 같은 이름, 다른 매개변수의 메서드를 여러 개 정의
    • 오버라이딩(Overriding): 부모 클래스의 메서드를 자식 클래스가 재정의하여 실행 내용 변경

🔁 오버라이딩과 virtual / override 키워드

  • virtual: 부모 클래스에서 해당 메서드가 오버라이딩 가능하다고 명시
  • override: 자식 클래스에서 부모의 virtual 메서드를 재정의할 때 사용
class Unit
{
    public virtual void Move() => Console.WriteLine("두 발로 걷기");
}

class Zergling : Unit
{
    public override void Move() => Console.WriteLine("네 발로 걷기");
}
  • 실행 예시
List<Unit> units = new List<Unit>();
units.Add(new Unit());
units.Add(new Zergling());

foreach (Unit unit in units)
{
    unit.Move();  // 각각 다르게 출력됨
}

🧰 추상 클래스와 추상 메서드

  • 추상 클래스 (abstract class)
    직접 객체를 만들 수 없고, 반드시 상속받아 구현해야 하는 클래스
  • 추상 메서드 (abstract method)
    본문 없이 선언만 존재하며, 자식 클래스가 반드시 오버라이딩해야 함
abstract class Shape
{
    public abstract void Draw();
}

class Circle : Shape
{
    public override void Draw() => Console.WriteLine("원을 그린다");
}
  • 사용 예
List<Shape> shapes = new List<Shape> { new Circle(), new Square(), new Triangle() };

foreach (Shape shape in shapes)
{
    shape.Draw();  // 각각의 도형에 맞게 동작
}

🔄 오버로딩과 오버라이딩 비교

구분 오버로딩(Overloading) 오버라이딩(Overriding)
정의 같은 이름, 다른 매개변수 부모 메서드 재정의
상속 필요 없음 반드시 상속 관계 필요
키워드 없음 virtual, override 사용
예시    
// 오버로딩
int Add(int a, int b) => a + b;
int Add(int a, int b, int c) => a + b + c;

// 오버라이딩
public class Shape
{
    public virtual void Draw() => Console.WriteLine("도형 그리기");
}

public class Circle : Shape
{
    public override void Draw() => Console.WriteLine("원 그리기");
}

 

반응형

C# - 틱택토 게임

Dev_Jen
|2025. 7. 7. 15:10
반응형
using System;

class Program
{
    static char[] board = { '1', '2', '3', '4', '5', '6', '7', '8', '9' };
    static int turn = 1; // 홀수: X, 짝수: O
    static bool gameEnded = false;

    static void Main(string[] args)
    {
        while (!gameEnded)
        {
            Console.Clear();
            DisplayBoard();
            PlayerTurn();
            CheckWin();
        }

        Console.WriteLine("게임이 종료되었습니다.");
    }

    static void DisplayBoard()
    {
        Console.WriteLine("\n틱택토 게임");
        Console.WriteLine("현재 보드 상태:");
        Console.WriteLine();
        Console.WriteLine($" {board[0]} | {board[1]} | {board[2]} ");
        Console.WriteLine("---|---|---");
        Console.WriteLine($" {board[3]} | {board[4]} | {board[5]} ");
        Console.WriteLine("---|---|---");
        Console.WriteLine($" {board[6]} | {board[7]} | {board[8]} ");
        Console.WriteLine();
    }

    static void PlayerTurn()
    {
        char player = (turn % 2 == 1) ? 'X' : 'O';
        Console.WriteLine($"{player}의 차례입니다. 위치(1~9)를 입력하세요:");

        int choice;
        bool validInput = int.TryParse(Console.ReadLine(), out choice);

        if (!validInput || choice < 1 || choice > 9 || board[choice - 1] == 'X' || board[choice - 1] == 'O')
        {
            Console.WriteLine("잘못된 입력입니다. 엔터를 눌러 다시 시도하세요.");
            Console.ReadLine();
            return;
        }

        board[choice - 1] = player;
        turn++;
    }

    static void CheckWin()
    {
        int[,] winConditions = new int[,]
        {
            { 0,1,2 }, { 3,4,5 }, { 6,7,8 }, // 가로
            { 0,3,6 }, { 1,4,7 }, { 2,5,8 }, // 세로
            { 0,4,8 }, { 2,4,6 }             // 대각선
        };

        for (int i = 0; i < 8; i++)
        {
            int a = winConditions[i, 0];
            int b = winConditions[i, 1];
            int c = winConditions[i, 2];

            if (board[a] == board[b] && board[b] == board[c])
            {
                Console.Clear();
                DisplayBoard();
                Console.WriteLine($"{board[a]} 승리!");
                gameEnded = true;
                return;
            }
        }

        if (turn > 9)
        {
            Console.Clear();
            DisplayBoard();
            Console.WriteLine("무승부입니다!");
            gameEnded = true;
        }
    }
}

 

 

🎯 만들면서 느낀 점

틱택토 게임을 직접 만들어보면서, 단순한 기능 구현 자체보다 게임의 구조를 어떻게 설계하고 구현해 나갈지 고민하는 과정이 가장 중요하다는 것을 느꼈다.
처음엔 간단하게 생각했지만, 실제로 구현하려고 하다 보니 전체 흐름을 미리 계획하고, 어떤 순서로 기능을 쌓아갈지 구조적인 틀을 잡는 일이 결코 만만하지 않았다.

그 과정을 통해 계획의 중요성, 그리고 작은 기능부터 차근차근 쌓아가는 개발의 기본 원칙을 다시금 체감할 수 있었다.

또한, 이번 구현 과정에서 TryParse라는 메서드를 처음 접했는데, 사용자 입력을 안전하게 숫자로 변환할 수 있어 매우 유용하다는 점을 배웠다.

반응형
반응형

🧩 메서드란 무엇인가?

✅ 메서드의 정의

메서드(Method)는 프로그램에서 특정한 작업을 수행하도록 정의된 코드 블록이다.
복잡한 코드를 기능별로 분리하여 모듈화함으로써 코드의 재사용성, 가독성, 유지보수성을 높일 수 있다.

C#에서 메서드는 클래스나 구조체 내에 정의되며, 외부에서 호출할 수 있다. 메서드는 일반적으로 다음과 같은 역할을 수행한다.


✨ 메서드의 주요 특징

  • 코드 재사용성
    반복적으로 사용되는 코드를 메서드로 정의해 한 번만 작성하면 여러 곳에서 호출하여 사용할 수 있다.
  • 모듈화(Modularity)
    프로그램을 논리적인 단위로 나눌 수 있다. 각 메서드는 하나의 작업에만 집중하도록 설계하는 것이 좋다.
  • 가독성 향상
    메서드 이름만 보고 어떤 동작을 수행하는지 예측 가능하므로 코드 전체의 흐름을 이해하기 쉬워진다.
  • 유지보수 용이성
    버그 수정이나 기능 변경 시, 메서드 단위로 수정이 가능하므로 다른 코드에 미치는 영향을 최소화할 수 있다.
  • 중복 제거
    동일하거나 유사한 코드가 여러 번 반복되는 경우, 하나의 메서드로 추출해 중복을 제거할 수 있다.

🧠 왜 메서드가 필요한가?

초보 개발자가 작성한 코드에서 가장 흔히 나타나는 문제는 같은 로직을 반복해서 작성하는 것이다.
이때 메서드를 사용하면 아래와 같은 이점을 누릴 수 있다.

 

기존 코드 문제 메서드를 사용할 경우
반복되는 코드로 가독성 저하 하나의 메서드로 대체 가능
수정할 때 모든 부분을 수정해야 함 메서드 하나만 수정하면 됨
기능 구분이 어려움 각 기능별로 나눠서 구성 가능
 

📌 예시: 메서드 없이 작성한 코드

Console.WriteLine("안녕하세요!");
Console.WriteLine("안녕하세요!");
Console.WriteLine("안녕하세요!");

📌 예시: 메서드 사용 후

void PrintGreeting() {
    Console.WriteLine("안녕하세요!");
}

// 호출
PrintGreeting();
PrintGreeting();
PrintGreeting();

→ 코드의 길이는 줄고, 가독성은 향상된다.

 

🧩 메서드 선언과 호출

✅ 메서드를 선언하는 방법

C#에서 메서드는 다음과 같은 구조로 선언한다.

[접근 제한자] [반환 타입] [메서드 이름]([매개변수 목록])
{
    // 실행할 코드
}

예를 들어,

public int Add(int a, int b)
{
    return a + b;
}

위의 예시는 Add라는 이름의 메서드를 정의한 것이다.
이 메서드는 두 개의 정수 매개변수를 받아 합산한 결과를 정수형으로 반환한다.


✨ 각 구성 요소 설명

  • 접근 제한자 (Access Modifier)
    외부에서 이 메서드를 사용할 수 있는지 여부를 결정한다.
    • public: 클래스 외부에서도 접근 가능
    • private: 선언된 클래스 내부에서만 사용 가능 (기본값)
    • protected, internal 등도 존재
  • 반환 타입 (Return Type)
    메서드가 실행 후 반환하는 데이터의 자료형을 나타낸다.
    • 예: int, string, bool, void 등
    • 반환값이 없을 경우 void를 사용
  • 메서드 이름 (Method Name)
    메서드를 식별하는 이름이다. 호출 시 이 이름을 사용한다.
    • 예: PrintMessage, CalculateSum 등
  • 매개변수 목록 (Parameter List)
    메서드 실행에 필요한 외부 데이터를 전달받는 변수 목록이다.
    • 자료형과 변수명을 함께 정의한다
    • 0개 이상 정의 가능하며, 쉼표(,)로 구분

📌 예제: 다양한 메서드 선언

// 반환값이 없는 메서드
public void SayHello()
{
    Console.WriteLine("안녕하세요!");
}

// 매개변수를 받는 메서드
public void GreetUser(string name)
{
    Console.WriteLine($"{name}님, 반갑습니다!");
}

// 반환값이 있는 메서드
public int Multiply(int a, int b)
{
    return a * b;
}

🧪 메서드 호출 방법

메서드를 호출하려면 메서드 이름 뒤에 괄호를 열고, 필요한 매개변수를 넣는다.

메서드이름(전달할 인자값);

예제

SayHello();                      // 매개변수 없음
GreetUser("재은");              // 문자열 매개변수 전달
int result = Multiply(3, 4);     // 반환값 사용

💡 주의사항

  • 호출 시 전달하는 인자의 개수, 순서, 자료형은 메서드 선언과 반드시 일치해야 한다.
  • 메서드는 호출 전에는 실행되지 않으며, 반드시 호출해야 코드가 실행된다.
  • void 메서드는 값을 반환하지 않으므로 별도로 변수에 저장하지 않는다.

📘 실전 예제

void PrintStars(int count)
{
    for (int i = 0; i < count; i++)
    {
        Console.Write("*");
    }
    Console.WriteLine();
}

// 호출
PrintStars(10);
PrintStars(5);

 

 

🧩 매개변수와 반환값

✅ 매개변수(Parameter)란?

매개변수는 메서드가 외부로부터 데이터를 받아들일 수 있도록 정의된 변수이다.
메서드를 호출할 때, 전달된 값을 메서드 내부에서 사용할 수 있게 해준다.


✨ 매개변수의 특징

  • 메서드 선언부에 자료형 + 변수명 형태로 정의한다.
  • 메서드 호출 시 괄호 안에 인자를 전달하면, 해당 인자가 매개변수로 복사된다.
  • 여러 개의 매개변수를 사용할 수 있으며, 콤마(,)로 구분한다.
  • 매개변수는 메서드 내에서 지역 변수처럼 사용된다.

📌 예제: 매개변수를 사용하는 메서드

void PrintMessage(string name)
{
    Console.WriteLine($"{name}님, 반갑습니다!");
}

// 호출
PrintMessage("재은");  // 출력: 재은님, 반갑습니다!

✅ 반환값(Return Value)이란?

반환값은 메서드가 작업을 수행한 후 결과를 호출한 곳으로 돌려주는 값이다.
반환값이 있는 메서드는 return 키워드를 사용하여 값을 반환한다.


✨ 반환값의 특징

  • 반환값의 자료형은 메서드 선언부의 반환 타입과 반드시 일치해야 한다.
  • 메서드가 return을 만나면 즉시 종료되며, 값을 호출한 곳에 돌려준다.
  • void를 사용하는 경우에는 반환값이 없으며, return;만 사용해 메서드를 종료할 수 있다.

📌 예제: 반환값이 있는 메서드

int Add(int a, int b)
{
    return a + b;
}

// 호출
int sum = Add(10, 20);
Console.WriteLine("합계: " + sum);  // 출력: 합계: 30

✅ void 메서드

void는 메서드가 반환값이 없다는 의미로 사용된다.
단순한 출력이나 특정 작업을 수행할 때 주로 사용한다.

void ShowNotice()
{
    Console.WriteLine("업데이트가 완료되었습니다.");
}

// 호출
ShowNotice();

🧪 다양한 활용 예제

// 단순 출력
void PrintLine()
{
    Console.WriteLine("====================");
}

// 반복 출력
void PrintLineCount(int count)
{
    for (int i = 0; i < count; i++)
    {
        Console.Write("=");
    }
    Console.WriteLine();
}

// 합계 계산
int GetTotal(int x, int y)
{
    return x + y;
}

// 호출
PrintLine();
PrintLineCount(10);

int result = GetTotal(100, 250);
Console.WriteLine("총합: " + result);

🧠 정리

개념 설명
매개변수 메서드 외부에서 전달되는 입력 값
반환값 메서드 실행 결과로 돌려주는 값
void 반환값이 없는 메서드에 사용하는 키워드

 

🧩 메서드 오버로딩 (Method Overloading)

✅ 메서드 오버로딩이란?

메서드 오버로딩이란 하나의 메서드 이름으로 여러 개의 메서드를 정의하는 기능이다.
매개변수의 개수, 자료형, 순서가 다르면 동일한 이름의 메서드를 중복해서 선언할 수 있다.


✨ 오버로딩의 조건

다음 중 하나라도 다르면 오버로딩이 가능하다.

  • 매개변수의 개수가 다를 경우
  • 매개변수의 자료형이 다를 경우
  • 매개변수의 순서가 다를 경우

⚠️ 단, 반환형만 다르게 하는 것은 오버로딩이 아니다.


📌 예제: 오버로딩된 메서드들

void PrintInfo(string name)
{
    Console.WriteLine($"이름: {name}");
}

void PrintInfo(string name, int age)
{
    Console.WriteLine($"이름: {name}, 나이: {age}");
}

void PrintInfo(int age, string name)
{
    Console.WriteLine($"나이: {age}, 이름: {name}");
}

위 예시에서는 모두 PrintInfo라는 이름의 메서드지만,
매개변수의 개수, 순서, 자료형이 다르기 때문에 오버로딩이 가능하다.


🧪 예제: 오버로딩을 활용한 덧셈 메서드

int Add(int a, int b)
{
    return a + b;
}

int Add(int a, int b, int c)
{
    return a + b + c;
}

double Add(double a, double b)
{
    return a + b;
}

// 호출 예시
int sum1 = Add(1, 2);           // 2개 정수
int sum2 = Add(1, 2, 3);        // 3개 정수
double sum3 = Add(1.5, 2.5);    // 2개 실수

Console.WriteLine(sum1);  // 출력: 3
Console.WriteLine(sum2);  // 출력: 6
Console.WriteLine(sum3);  // 출력: 4.0

🔍 메서드 오버로딩의 장점

  • 가독성 향상: 같은 의미의 작업을 하나의 이름으로 통일할 수 있다.
  • 코드 일관성 유지: 함수 이름을 계속 바꿀 필요 없이 매개변수만 다르게 작성하면 된다.
  • 유연성 증가: 다양한 형태의 인자 처리 가능

🧠 정리

구분 오버로딩 가능 여부
매개변수 개수 다름 가능
매개변수 자료형 다름 가능
매개변수 순서 다름 가능
반환형만 다름 불가능
 

 

🔁 재귀 호출 (Recursive Call)

✅ 재귀 호출이란?

**재귀 호출(Recursion)**은 메서드가 자기 자신을 다시 호출하는 방식이다.
문제를 더 작은 단위로 나누어 같은 방법으로 반복 처리할 수 있도록 한다.


✨ 재귀 호출의 기본 구조

void 함수이름(매개변수)
{
    if (종료 조건)
    {
        // 재귀 종료 시 실행할 코드
    }
    else
    {
        // 자기 자신 호출 (재귀)
        함수이름(매개변수 변화);
    }
}
  • 반드시 **종료 조건(탈출 조건)**이 있어야 한다.
  • 종료 조건이 없으면 무한 루프로 인해 Stack Overflow 오류가 발생한다.

📌 예제: 카운트다운

void CountDown(int n)
{
    if (n <= 0)
    {
        Console.WriteLine("카운트 종료");
    }
    else
    {
        Console.WriteLine(n);
        CountDown(n - 1);  // 재귀 호출
    }
}

// 호출
CountDown(5);

출력 결과

 
5  
4  
3  
2  
1  
카운트 종료

🔍 재귀 호출의 동작 원리

  • 재귀 호출이 발생하면 호출한 메서드는 대기 상태가 되고,
    새로운 메서드가 스택에 쌓인다.
  • 각 메서드 호출이 종료 조건을 만나면 하나씩 스택에서 제거되며 복귀한다.

📌 예제: 팩토리얼 (Factorial)

int Factorial(int n)
{
    if (n <= 1)
        return 1;
    else
        return n * Factorial(n - 1);
}

// 호출
int result = Factorial(5);
Console.WriteLine(result);  // 출력: 120
  • 5! = 5 × 4 × 3 × 2 × 1을 재귀적으로 계산하는 구조
  • 재귀 호출이 반복되며 점점 작은 문제로 나아가고,
    마지막에 하나씩 계산되어 돌아온다.

⚠️ 주의할 점

  • 종료 조건을 반드시 정확하게 설정해야 한다.
  • 너무 깊은 재귀 호출은 **스택 메모리 초과(Stack Overflow)**를 유발할 수 있다.
  • 복잡한 재귀는 반복문으로 대체하는 것이 더 효율적인 경우도 있다.

🧠 정리

항목 내용
정의 메서드가 자기 자신을 다시 호출하는 방식
장점 문제를 단순하고 직관적으로 표현 가능
단점 과도한 재귀는 스택 오버플로우 위험 있음
필수 요소 종료 조건 존재 필요

 

🛠️ 메서드 활용 사례

✅ 메서드는 왜 사용하는가?

메서드는 단순히 코드를 줄이기 위한 도구가 아니라,
코드의 품질을 높이고 유지보수를 용이하게 만들기 위한 핵심 구조이다.


📌 메서드를 사용해야 하는 이유

● 코드의 재사용성

  • 동일한 작업을 여러 번 구현할 필요 없이, 메서드로 정의한 뒤 필요할 때마다 호출하여 사용 가능하다.
  • 반복되는 작업을 메서드로 추출하면 코드의 중복을 제거할 수 있다.

● 가독성 향상

  • 메서드는 코드를 의미 단위로 분리해주므로 전체 구조를 파악하기 쉬워진다.
  • 메서드 이름만 보고도 해당 블록의 역할을 빠르게 이해할 수 있다.

● 유지보수의 편리함

  • 특정 기능을 수정할 때 해당 메서드만 수정하면 되므로, 전체 코드에 영향을 주지 않는다.
  • 기능이 독립적으로 분리되어 있어 디버깅 및 테스트가 용이하다.

📎 예제: 메서드 없이 구현한 코드

double radius = 5.0;
double circleArea = Math.PI * radius * radius;

double width = 3.0;
double height = 4.0;
double rectangleArea = width * height;

Console.WriteLine("원의 넓이: " + circleArea);
Console.WriteLine("사각형의 넓이: " + rectangleArea);
  • 간단한 예시지만, 계산 로직이 메인 흐름과 뒤섞여 있어 확장성과 재사용성이 떨어진다.

📎 예제: 메서드를 활용한 코드 개선

double CalculateCircleArea(double radius)
{
    return Math.PI * radius * radius;
}

double CalculateRectangleArea(double width, double height)
{
    return width * height;
}

// 호출부
double circleArea = CalculateCircleArea(5.0);
double rectangleArea = CalculateRectangleArea(3.0, 4.0);

Console.WriteLine("원의 넓이: " + circleArea);
Console.WriteLine("사각형의 넓이: " + rectangleArea);
  • 계산 로직이 메서드로 분리되어 의도가 명확하고 유지보수가 쉬워졌다.
  • 다른 곳에서도 이 메서드를 재사용할 수 있다.

🔍 실전에서의 활용 사례

기능 예시 메서드 활용 방안
플레이어 스탯 계산 CalculatePlayerStats() 메서드로 분리
UI 업데이트 UpdateUI() 메서드로 분리
아이템 효과 적용 ApplyItemEffect(Item item) 메서드 활용
입력 처리 HandleInput() 메서드로 일관 처리
 

🧠 정리

항목 설명
재사용성 반복되는 코드 제거, 호출만으로 재활용 가능
가독성 코드 블록을 명확히 나눠 목적 파악이 쉬움
유지보수 변경이 필요할 때 해당 메서드만 수정하면 됨
확장성 기능이 명확히 분리되어 새로운 기능 추가 용이

 

🧱 구조체 (Struct)

✅ 구조체란?

**구조체(Struct)**는 여러 데이터를 하나로 묶어서 새로운 사용자 정의 자료형을 만드는 방식이다.
C#에서는 구조체를 통해 관련된 데이터를 하나의 단위로 관리할 수 있으며, 클래스와 유사하지만 값 타입이라는 차이가 있다.


🧩 구조체의 특징

항목 설명
키워드 struct
타입 구분 값 타입 (Value Type)
저장 위치 스택(Stack)에 저장
상속 여부 클래스와 달리 구조체는 상속이 불가능
기본 생성자 기본 생성자를 직접 정의할 수 없음 (컴파일러가 자동 생성)
용도 데이터 묶음, 경량 객체 등 성능이 중요한 상황에서 사용
 

🛠️ 구조체 선언 예시

struct Person
{
    public string Name;
    public int Age;

    public void PrintInfo()
    {
        Console.WriteLine($"이름: {Name}, 나이: {Age}");
    }
}
  • Person이라는 구조체는 이름과 나이를 하나의 단위로 묶은 사용자 정의 자료형이다.
  • PrintInfo() 메서드를 포함할 수 있어 간단한 동작도 구조체 내부에서 처리 가능하다.

🧪 구조체 사용 예시

Person person1;
person1.Name = "재은";
person1.Age = 26;
person1.PrintInfo();

출력

이름: 재은, 나이: 26

🧠 클래스와 구조체의 차이점

항목 클래스 구조체
타입 참조 타입 (Reference Type) 값 타입 (Value Type)
저장 위치 힙 (Heap) 스택 (Stack)
상속 가능 여부 가능 불가능
기본 생성자 정의 가능 정의 불가 (자동 생성)
성능 상대적으로 느림 상대적으로 빠름
용도 복잡한 기능, 상속 활용 간단한 데이터 묶음, 성능 중시
 

📎 구조체 활용 사례

  • 좌표 정보: Point, Vector2, Vector3
  • 게임 내 스탯 구조: PlayerStats, ItemData
  • 색상 정보: Color
  • 시간 정보: DateTime (구조체 기반)

⚠️ 구조체 사용 시 주의점

  • 구조체는 값 타입이기 때문에 복사 시 전체 값이 복사된다.
  • 복사 비용이 큰 경우 (예: 멤버가 너무 많거나 무거운 객체일 때)는 클래스가 더 적합하다.
  • 구조체 내부에서 복잡한 상태를 관리하거나 상속이 필요할 경우는 클래스를 사용하는 것이 바람직하다.

✅ 정리

핵심 포인트 설명
값 타입 구조 스택에 저장되어 성능이 좋음
경량 객체에 적합 좌표, 색상, 간단한 정보 구조
클래스보다 제한적 상속 불가, 생성자 제약 등 있음
데이터 묶음에 용이 여러 데이터를 하나의 단위로 관리 가능

 

 

근데 갑자기 문득 궁금해졌다 구조체는 값 타입이라고 하니 그럼 기본적으로 스택에 저장이 되겠구나 싶었다.

근데 GPT에게 물어보니..

✅ 정리

사용 방식 저장 위치
지역 변수로 선언한 구조체 스택
클래스의 필드로 구조체 포함
박싱된 구조체
new 키워드로 구조체 초기화 스택

이렇게 얘기하는것이 아니겠나..흠..그러면 여러 조건으로 물어봤을때는 어떻게 대답하는지 궁금하다.

Q. 오 그러면 구조체를 클래스 내에서 선언하고 사용하면 힙에 저장되고, 구조체를 오브젝트 타입으로 변수를 선언하면 힙에 저장되고(박싱), new 키워드로 구조체를 초기화하면 스택으로 존재한다는거지? 근데 만약 클래스 내에서 하면 힙이고?

 

✅ 구조체의 저장 위치: 스택 vs 힙 정리

1. 구조체가 지역 변수일 때 (함수 안에서 선언)

void Test()
{
    PlayerState player = new PlayerState(); // 구조체
}
  • 구조체는 값 타입이기 때문에 스택(stack) 에 저장됨.
  • new 키워드는 힙 할당이 아니라, 필드 초기화만 해줌.

🔹 new를 썼다고 힙에 저장되는 것이 아님!


2. 구조체가 클래스의 필드일 때

class Game
{
    public PlayerState player;
}
  • Game 객체는 힙(heap) 에 저장되며,
  • 그 안에 있는 구조체 필드(player)도 힙 안에 포함됨.

🔹 구조체의 위치는 해당 구조체가 포함된 객체의 위치를 따름.


3. 구조체를 object로 저장 (박싱)

object obj = new PlayerState();
  • 구조체는 값 타입이기 때문에 object와 같은 참조 타입에 저장할 때 박싱(Boxing) 이 발생함.
  • 이 경우, 구조체 복사본이 힙에 새로 생성됨.

🔹 object, interface 등에 구조체를 담으면 힙에 박싱됨.


📌 결론 요약

선언 위치/사용 방식 저장 위치 비고
지역 변수로 선언된 구조체 스택 기본적인 값 타입 동작
new 키워드로 초기화된 구조체 스택 단순 필드 초기화
클래스의 필드로 포함된 구조체 클래스가 힙에 저장되기 때문
구조체를 object로 박싱했을 때 박싱 발생
구조체 배열의 경우 (배열이 힙에 있음) 배열은 참조 타입이라 힙에 생성됨

 

흠.. 또 궁금한게 생겼습니다 클래스 내에서 구조체를 선언하고 사용하면 힙에 저장된다고 하는데 기본적으로 구조체는 스택에 저장된다고 하니까 근데 그럼 구조체는 값을 스택에 저장된다고 했는데 그러면 클래스 내에서 구조체를 선언하고 사용하면 힙에 구조체의 주소값을 가지고있고 스택에 값을 저장하는 것인가? 라는 궁금증이 생겼다.

 

 

❌ “힙에 구조체의 주소값이 있고, 값은 스택에 있다” → ❌ 틀린 표현이야.

구조체는 값 타입이기 때문에, 그 값 자체가 어디에 저장되느냐가 중요해.
값 타입은 참조가 아니라 실제 데이터를 그 자리에 복사해서 보관하는 성격이야.


✅ 정답: 클래스의 필드로 구조체가 있을 경우 → 구조체 전체 값이 힙에 존재한다.

예시로 보면:

struct PlayerState
{
    public int hp;
    public int mp;
}

class Game
{
    public PlayerState state;
}

Game game = new Game();
  • game 객체는 new로 생성되므로 힙(heap) 에 저장됨.
  • 그 안에 있는 state 필드는 구조체니까 값 타입이지만,
  • 클래스 내부에 포함되어 있으므로 state의 값 전체가 힙에 함께 저장된다.

👉 이때 힙에 있는 건 구조체의 “주소”가 아니라 “실제 값”이야.


💡 정리

상황 구조체의 위치 저장되는 것
지역 변수로 선언됨 스택 구조체의
클래스의 필드로 선언됨 힙 (클래스 내부) 구조체의
object에 박싱됨 힙 (박싱된 객체 내부) 구조체의

 

 

📌 혼동주의

구조체는 참조 타입처럼 “주소만 저장되고 값은 따로” 있는 게 아님.
구조체는 항상 값 자체가 그 위치에 복사되어 저장됨.

 

오케이! 궁금증 해결!

반응형
반응형

📘 배열과 컬렉션


배열

1차원 배열

동일한 데이터 유형을 연속적으로 저장하며, 인덱스를 통해 요소에 접근할 수 있다. 배열은 선언 시 고정된 크기를 가지며, 크기 이상의 데이터는 저장할 수 없다.

int[] array1 = new int[5];
array1[0] = 1;
...
...

예제: 배열을 활용한 합계 계산

int[] itemPrices = { 100, 200, 300, 400, 500 };
int totalPrice = 0;
for (int i = 0; i < itemPrices.Length; i++)
{
    totalPrice += itemPrices[i];
}
Console.WriteLine("총 아이템 가격: " + totalPrice + " gold");

배열 실습 예시

  • 게임 캐릭터 능력치 배열
  • 학생 성적 평균 계산
  • 배열 기반 숫자 맞추기 게임

다차원 배열

행과 열로 구성된 2차원 이상의 배열 구조로, 표 형태의 데이터를 처리할 때 사용된다.

int[,] array2D = new int[2, 3];
int[,,] array3D = new int[2, 3, 4];

예제: 2차원 배열을 활용한 게임 맵

int[,] map = {
    {1,1,1,1,1},
    {1,0,0,0,1},
    ...
};

컬렉션

컬렉션은 배열과 유사하지만, 크기가 가변적이며 다양한 기능을 지원하는 자료 구조다. C#의 컬렉션은 System.Collections.Generic 네임스페이스에 정의되어 있다.

List

동적으로 크기가 조정되는 배열 구조로, 요소의 추가/삭제/삽입이 가능하다. 내부적으로 배열을 기반으로 동작하며, 요소가 많아질 경우 새로운 배열을 생성해 데이터를 복사하는 방식으로 확장된다.

List<int> numbers = new List<int>();
numbers.Add(1); numbers.Remove(1);

Dictionary

키와 값의 쌍으로 데이터를 저장하는 구조다. 키는 중복될 수 없으며, 값에 빠르게 접근할 수 있는 장점이 있다.

Dictionary<string, int> scores = new Dictionary<string, int>();
scores.Add("Alice", 100);

Stack

후입선출(LIFO) 구조로 동작하며, Push()로 추가하고 Pop()으로 제거한다.

Stack<int> stack = new Stack<int>();
stack.Push(1); int val = stack.Pop();

Queue

선입선출(FIFO) 구조로 동작하며, Enqueue()로 추가하고 Dequeue()로 제거한다.

Queue<int> queue = new Queue<int>();
queue.Enqueue(1); int val = queue.Dequeue();

HashSet

중복을 허용하지 않는 집합 구조다. 삽입 순서가 보장되지 않으며, Contains()로 존재 여부를 확인할 수 있다.

HashSet<int> set = new HashSet<int>();
set.Add(1); set.Contains(1);

배열 vs 리스트

항목 배열 리스트
크기 고정 가변
메모리 정적 할당 동적 배열 구조
기능 단순 저장/접근 삽입, 삭제, 탐색 등 다양
성능 반복에 유리 유연성 뛰어남
 
  • 데이터 크기가 고정되거나 반복 연산 중심일 경우 배열 사용이 유리하다.
  • 유동적인 데이터 처리, 삽입/삭제가 잦은 경우 리스트를 사용하는 것이 효율적이다.

 

게임케릭터 능력치 만들기

// 플레이어의 공격력, 방어력, 체력, 스피드를 저장할 배열
int[] playerStats = new int[4]; 

// 능력치를 랜덤으로 생성하여 배열에 저장
Random rand = new Random();
for (int i = 0; i < playerStats.Length; i++)
{
    playerStats[i] = rand.Next(1, 11);
}

// 능력치 출력
Console.WriteLine("플레이어의 공격력: "  + playerStats[0]);
Console.WriteLine("플레이어의 방어력: "  + playerStats[1]);
Console.WriteLine("플레이어의 체력: "    + playerStats[2]);
Console.WriteLine("플레이어의 스피드: "  + playerStats[3]);

 

학생들의 성적 평균 구하기

int[] scores = new int[5];  // 5명의 학생 성적을 저장할 배열

// 성적 입력 받기
for (int i = 0; i < scores.Length; i++)
{
    Console.Write("학생 " + (i + 1) + "의 성적을 입력하세요: ");
    scores[i] = int.Parse(Console.ReadLine());
}

// 성적 총합 계산
int sum = 0;
for (int i = 0; i < scores.Length; i++)
{
    sum += scores[i];
}

// 성적 평균 출력
double average = (double)sum / scores.Length;
Console.WriteLine("성적 평균은 " + average + "입니다.");

 

배열을 활용한 숫자 맞추기 게임

Random random = new Random();  // 랜덤 객체 생성
int[] numbers = new int[3];  // 3개의 숫자를 저장할 배열

// 3개의 랜덤 숫자 생성하여 배열에 저장
for (int i = 0; i < numbers.Length; i++)
{
    numbers[i] = random.Next(1, 10);
}

int attempt = 0;  // 시도 횟수 초기화
while (true)
{
    Console.Write("3개의 숫자를 입력하세요 (1~9): ");
    int[] guesses = new int[3];  // 사용자가 입력한 숫자를 저장할 배열

    // 사용자가 입력한 숫자 배열에 저장
    for (int i = 0; i < guesses.Length; i++)
    {
        guesses[i] = int.Parse(Console.ReadLine());
    }

    int correct = 0;  // 맞춘 숫자의 개수 초기화

    // 숫자 비교 및 맞춘 개수 계산
    for (int i = 0; i < numbers.Length; i++)
    {
        for (int j = 0; j < guesses.Length; j++)
        {
            if (numbers[i] == guesses[j])
            {
                correct++;
                break;
            }
        }
    }

    attempt++;  // 시도 횟수 증가
    Console.WriteLine("시도 #" + attempt + ": " + correct + "개의 숫자를 맞추셨습니다.");

    // 모든 숫자를 맞춘 경우 게임 종료
    if (correct == 3)
    {
        Console.WriteLine("축하합니다! 모든 숫자를 맞추셨습니다.");
        break;
    }
}

 

게임 맵을 구현

int[,] map = new int[5, 5] 
{ 
    { 1, 1, 1, 1, 1 }, 
    { 1, 0, 0, 0, 1 }, 
    { 1, 0, 1, 0, 1 }, 
    { 1, 0, 0, 0, 1 }, 
    { 1, 1, 1, 1, 1 } 
};

for (int i = 0; i < 5; i++)
{
    for (int j = 0; j < 5; j++)
    {
        if (map[i, j] == 1)
        {
            Console.Write("■ ");
        }
        else
        {
            Console.Write("□ ");
        }
    }
    Console.WriteLine();
}

 

반응형
반응형

📘 조건문과 반복문 1 - if, else if, 중첩 조건문, switch, 삼항 연산자


🔍 조건문 개요

조건문은 주어진 조건식의 결과에 따라 프로그램의 제어 흐름을 변경하는 제어문이다.


if 문

if 문은 조건식이 참일 경우에만 해당 블록을 실행한다.

if (조건식)
{
    // 조건식이 참일 경우 실행할 코드
}

한 줄일 경우 중괄호 생략 가능:

if (조건식)
    Console.WriteLine("실행");

예시:

int playerScore = 80;

if (playerScore >= 70) 
{
    Console.WriteLine("플레이어의 점수는 70점 이상입니다. 합격입니다!");
}
Console.WriteLine("프로그램이 종료됩니다.");

else 문

if 문의 조건이 거짓일 경우 실행할 코드를 지정할 때 사용한다.

if (조건식)
{
    // 조건식이 참일 경우
}
else
{
    // 조건식이 거짓일 경우
}

예시:

int itemCount = 5;
string itemName = "HP 포션";

if (itemCount > 0)
{
    Console.WriteLine($"보유한 {itemName}의 수량: {itemCount}");
}
else
{
    Console.WriteLine($"보유한 {itemName}이 없습니다.");
}

else if 문

조건이 여러 개인 경우 순차적으로 조건식을 검사할 수 있도록 한다.

if (조건식1)
{
    // 조건식1이 참
}
else if (조건식2)
{
    // 조건식2가 참
}
else
{
    // 모든 조건이 거짓
}

예시:

int playerScore = 100;
string playerRank = "";

if (playerScore >= 90)
{
    playerRank = "Diamond";
}
else if (playerScore >= 80)
{
    playerRank = "Platinum";
}
else if (playerScore >= 70)
{
    playerRank = "Gold";
}
else if (playerScore >= 60)
{
    playerRank = "Silver";
}
else
{
    playerRank = "Bronze";
}

Console.WriteLine("플레이어의 등급은 " + playerRank + "입니다.");

중첩 조건문

조건문 내부에 또 다른 조건문을 포함한 구조로, 복잡한 분기 처리에 활용된다.

예시:

int itemLevel = 3;
string itemType = "Weapon";

if (itemType == "Weapon")
{
    if (itemLevel == 1)
    {
        Console.WriteLine("공격력이 10 증가했습니다.");
    }
    else if (itemLevel == 2)
    {
        Console.WriteLine("공격력이 20 증가했습니다.");
    }
    else
    {
        Console.WriteLine("잘못된 아이템 레벨입니다.");
    }
}
else if (itemType == "Armor")
{
    if (itemLevel == 1)
    {
        Console.WriteLine("방어력이 10 증가했습니다.");
    }
    else if (itemLevel == 2)
    {
        Console.WriteLine("방어력이 20 증가했습니다.");
    }
    else
    {
        Console.WriteLine("잘못된 아이템 레벨입니다.");
    }
}
else
{
    Console.WriteLine("잘못된 아이템 종류입니다.");
}

switch 문

변수나 식의 결과 값에 따라 실행할 코드를 구분하는 조건문이다.

switch (변수)
{
    case 값1:
        // 실행 코드
        break;
    case 값2:
        // 실행 코드
        break;
    default:
        // 일치하는 case가 없을 때
        break;
}

예시:

Console.WriteLine("1: 전사 / 2: 마법사 / 3: 궁수");
Console.Write("직업을 선택하세요: ");
string job = Console.ReadLine();

switch (job)
{
    case "1":
        Console.WriteLine("전사를 선택하셨습니다.");
        break;
    case "2":
        Console.WriteLine("마법사를 선택하셨습니다.");
        break;
    case "3":
        Console.WriteLine("궁수를 선택하셨습니다.");
        break;
    default:
        Console.WriteLine("올바른 값을 입력해주세요.");
        break;
}

삼항 연산자

조건식에 따라 두 값 중 하나를 선택하는 연산자이다.

(조건식) ? 참일 때 값 : 거짓일 때 값;

예시:

int currentExp = 1200;
int requiredExp = 2000;

string result = (currentExp >= requiredExp) ? "레벨업 가능" : "레벨업 불가능";
Console.WriteLine(result);

동일 로직을 if-else 문으로 표현하면 다음과 같다:

if (currentExp >= requiredExp)
{
    Console.WriteLine("레벨업 가능");
}
else
{
    Console.WriteLine("레벨업 불가능");
}

 

 

📘 조건문과 반복문 2 — 조건문 심화 실습


🔍 짝수 / 홀수 판별

% 나머지 연산자를 이용하여 입력받은 정수가 짝수인지 홀수인지 판별할 수 있다.
2로 나누었을 때 나머지가 0이면 짝수, 아니면 홀수이다.

Console.Write("번호를 입력하세요: ");
int number = int.Parse(Console.ReadLine());

if (number % 2 == 0)
{
    Console.WriteLine("짝수입니다.");
}
else
{
    Console.WriteLine("홀수입니다.");
}

🔍 등급 출력 (switch 활용)

기존 else if 방식 대신 switch 문을 사용하여 점수를 등급으로 변환한다.
정수 점수를 10으로 나눈 몫을 기준으로 분기를 나눈다.

int playerScore = 100;
string playerRank = "";

switch (playerScore / 10)
{
    case 10:
    case 9:
        playerRank = "Diamond";
        break;
    case 8:
        playerRank = "Platinum";
        break;
    case 7:
        playerRank = "Gold";
        break;
    case 6:
        playerRank = "Silver";
        break;
    default:
        playerRank = "Bronze";
        break;
}

Console.WriteLine("플레이어의 등급은 " + playerRank + "입니다.");

🔍 로그인 프로그램

논리 연산자 &&, ||를 활용해 아이디와 비밀번호를 확인한다.
모두 일치해야 로그인 성공으로 간주한다.

string id = "myid";
string password = "mypassword";

Console.Write("아이디를 입력하세요: ");
string inputId = Console.ReadLine();
Console.Write("비밀번호를 입력하세요: ");
string inputPassword = Console.ReadLine();

if (inputId == id && inputPassword == password)
{
    Console.WriteLine("로그인 성공!");
}
else
{
    Console.WriteLine("로그인 실패...");
}

🔍 알파벳 판별

문자 하나를 입력받아 알파벳인지 아닌지를 판별한다.
az, AZ 범위에 해당하면 알파벳이다.

Console.Write("문자를 입력하세요: ");
char input = Console.ReadLine()[0];

if (input >= 'a' && input <= 'z' || input >= 'A' && input <= 'Z')
{
    Console.WriteLine("알파벳입니다.");
}
else
{
    Console.WriteLine("알파벳이 아닙니다.");
}

 

 

 

📘 조건문과 반복문 3 — 반복문 구조 (for, while, do-while, foreach, 중첩 반복, break/continue)


🔁 for 문

for 문은 반복 횟수가 명확한 경우 사용한다.
초기식, 조건식, 증감식으로 구성되어 있다.

for (초기식; 조건식; 증감식)
{
    // 조건식이 참인 동안 실행되는 코드
}

예시

for (int i = 0; i < 10; i++) 
{
    Console.WriteLine(i);
}

짝수만 출력하는 예제:

for (int i = 0; i < 10; i += 2)
{
    Console.WriteLine(i);
}

🔁 while 문

while 문은 조건식이 참인 동안 계속 반복하며, 반복 횟수가 불명확할 때 유용하다.

while (조건식)
{
    // 조건식이 참이면 반복 실행
}

예시

int count = 0;

while (count < 10)
{
    Console.WriteLine("적을 처치했습니다! 남은 적 수: " + (10 - count - 1));
    count++;
}

Console.WriteLine("축하합니다! 게임에서 승리하셨습니다!");

🔁 for vs while 비교

int sum = 0;

// for 문
for (int i = 1; i <= 5; i++)
{
    sum += i;
}
Console.WriteLine("1부터 5까지의 합 (for): " + sum);

// while 문
sum = 0;
int j = 1;
while (j <= 5)
{
    sum += j;
    j++;
}
Console.WriteLine("1부터 5까지의 합 (while): " + sum);
  • for 문은 반복 조건이 고정된 경우에 적합하다.
  • while 문은 종료 조건이 외부에 의해 결정될 때 더 유연하다.

🔁 do-while 문

do-while 문은 조건식 검사 전에 코드 블록을 최소 1회 실행한다.

do
{
    // 최소 1번 실행
}
while (조건식);

예시

int sum = 0;
int num;

do
{
    Console.Write("숫자를 입력하세요 (0 입력 시 종료): ");
    num = int.Parse(Console.ReadLine());
    sum += num;
} while (num != 0);

Console.WriteLine("합계: " + sum);

🔁 foreach 문

foreach 문은 배열 또는 컬렉션의 요소를 순회할 때 사용된다.
인덱스를 직접 다루지 않아도 되므로 간결하고 안전하다.

foreach (자료형 변수 in 배열)
{
    // 각 요소에 대해 실행할 코드
}

예시

string[] inventory = { "검", "방패", "활", "화살", "물약" };

foreach (string item in inventory)
{
    Console.WriteLine(item);
}

🔁 중첩 반복문

반복문 안에 또 다른 반복문을 넣을 수 있다.
2차원 데이터나 구구단처럼 복합적인 패턴을 출력할 때 사용된다.

for (int i = 0; i < 5; i++)
{
    for (int j = 0; j < 3; j++)
    {
        Console.WriteLine("i: {0}, j: {1}", i, j);
    }
}

구구단 예시:

for (int i = 2; i <= 9; i++)
{
    for (int j = 1; j <= 9; j++)
    {
        Console.WriteLine(i + " x " + j + " = " + (i * j));
    }
}

⏹️ break 와 continue

  • break는 반복문을 즉시 종료한다.
  • continue는 현재 반복을 건너뛰고 다음 반복으로 진행한다.
for (int i = 1; i <= 10; i++)
{
    if (i % 3 == 0)
    {
        continue; // 3의 배수는 출력하지 않음
    }

    Console.WriteLine(i);

    if (i == 7)
    {
        break; // 7 이후 반복 종료
    }
}

무한 루프 종료 예시:

int sum = 0;

while (true)
{
    Console.Write("숫자를 입력하세요: ");
    int input = int.Parse(Console.ReadLine());

    if (input == 0)
    {
        Console.WriteLine("프로그램을 종료합니다.");
        break;
    }

    if (input < 0)
    {
        Console.WriteLine("음수는 무시합니다.");
        continue;
    }

    sum += input;
    Console.WriteLine("현재까지의 합: " + sum);
}

Console.WriteLine("합계: " + sum);

 

📘 조건문과 반복문 4 — 반복문 심화 실습


🎮 가위바위보 게임

while 문과 조건문을 활용한 반복 구조 실습 예제이다.
플레이어가 컴퓨터와 같은 선택을 할 때까지 게임을 반복한다.

string[] choices = { "가위", "바위", "보" };
string playerChoice = "";
string computerChoice = choices[new Random().Next(0, 3)];

while (playerChoice != computerChoice)
{
    Console.Write("가위, 바위, 보 중 하나를 선택하세요: ");
    playerChoice = Console.ReadLine();

    Console.WriteLine("컴퓨터: " + computerChoice);

    if (playerChoice == computerChoice)
    {
        Console.WriteLine("비겼습니다!");
    }
    else if ((playerChoice == "가위" && computerChoice == "보") ||
             (playerChoice == "바위" && computerChoice == "가위") ||
             (playerChoice == "보" && computerChoice == "바위"))
    {
        Console.WriteLine("플레이어 승리!");
    }
    else
    {
        Console.WriteLine("컴퓨터 승리!");
    }
}
  • Random().Next(0, 3)으로 컴퓨터의 선택을 무작위로 설정
  • while 조건을 통해 반복 여부를 제어
  • if, else if, else 구조를 활용한 승부 판단

🎯 숫자 맞추기 게임

1부터 100 사이의 난수를 생성하고, 플레이어가 해당 숫자를 맞출 때까지 계속해서 추측하도록 유도한다.
입력한 숫자가 정답보다 큰지, 작은지를 안내하며 시도 횟수도 함께 출력한다.

int targetNumber = new Random().Next(1, 101);
int guess = 0;
int count = 0;

Console.WriteLine("1부터 100 사이의 숫자를 맞춰보세요.");

while (guess != targetNumber)
{
    Console.Write("추측한 숫자를 입력하세요: ");
    guess = int.Parse(Console.ReadLine());
    count++;

    if (guess < targetNumber)
    {
        Console.WriteLine("좀 더 큰 숫자를 입력하세요.");
    }
    else if (guess > targetNumber)
    {
        Console.WriteLine("좀 더 작은 숫자를 입력하세요.");
    }
    else
    {
        Console.WriteLine("축하합니다! 숫자를 맞추셨습니다.");
        Console.WriteLine("시도한 횟수: " + count);
    }
}
  • Random().Next(1, 101)을 통해 1~100 사이의 정답 생성
  • 사용자 입력은 int.Parse(Console.ReadLine())로 처리
  • 시도 횟수는 count 변수를 통해 누적

 

 

반응형
반응형

사용자로부터 입력 받기

///이름, 나이 - 입력, 출력

Console.Write("이름 입력 : ");
string name = Console.ReadLine();
Console.Write("나이 입력 : ");
int age = int.Parse(Console.ReadLine());

Console.WriteLine($"이름 : {name}, 나이 : {age}");

 

간단한 사칙연산 계산기 만들기

Console.WriteLine("간단한 사칙연산 계산기 만들기");

Console.Write("첫 번째 숫자 입력 : ");
int num1 = int.Parse(Console.ReadLine());
Console.Write("두 번째 숫자 입력 : ");
int num2 = int.Parse(Console.ReadLine());


Console.WriteLine("더하기 : " + (num1 + num2));
Console.WriteLine("빼기 : " + (num1 - num2));
Console.WriteLine("곱하기 : " + (num1 * num2));
Console.WriteLine("나누기 - 몫 : " + (num1 / num2) +", 나머지 : "+ (num1 % num2));

 

온도 변환기 만들기 (섭씨온도 -> 화씨온도)

//온도 변환기 만들기
Console.Write("섭씨 온도 입력 : ");
float num = float.Parse(Console.ReadLine());

Console.Write("화씨 온도 : " + ((num*(9f/5f))+ 32f));

 

BMI 계산기 만들기

//BMI 계산기 만들기
Console.Write("키 입력 : ");
float cm = float.Parse(Console.ReadLine());
float m = cm / 100;
Console.Write("몸무게 입력 : ");
float kg = float.Parse(Console.ReadLine());

Console.WriteLine("당신의 BMI : " + (kg/(m*m)));

 

반응형
반응형

📘 형변환과 입력, 연산자 그리고 문자열 활용


🔄 형변환의 개념

C#은 형식이 엄격한 언어이기 때문에 서로 다른 자료형 간의 대입이나 연산을 수행할 때 형변환이 필요하다.
형변환에는 명시적 형변환암시적 형변환 두 가지 방식이 있다.

암시적 형변환 (Implicit Casting)

작은 범위의 자료형에서 큰 범위의 자료형으로 자동으로 변환된다.

int num = 10;
float result = num; // int → float 자동 변환

명시적 형변환 (Explicit Casting)

데이터 손실 우려가 있을 경우, 개발자가 직접 캐스팅을 명시해야 한다.

float pi = 3.14f;
int intPi = (int)pi; // 소수점 이하 버려짐

⌨️ Console 입력 받기

C#에서는 Console.ReadLine() 메서드를 통해 문자열 형태로 입력을 받을 수 있다.
입력된 문자열은 필요한 자료형으로 변환하여 사용해야 한다.

문자열 입력

Console.Write("Enter your name: ");
string name = Console.ReadLine();

숫자 입력 (문자열 → 정수형 변환)

Console.Write("Enter your age: ");
int age = int.Parse(Console.ReadLine());

공백으로 구분된 다중 입력

string input = Console.ReadLine();           // 예: "10 20"
string[] parts = input.Split(' ');
int a = int.Parse(parts[0]);
int b = int.Parse(parts[1]);

➕ 연산자 종류와 사용법

C#에서는 다양한 연산자를 제공한다. 자료형에 따라 연산자의 동작이 다르므로 정확한 사용이 필요하다.

산술 연산자

연산자 설명
+ 덧셈
- 뺄셈
* 곱셈
/ 나눗셈
% 나머지
 

관계 연산자

연산자 설명
== 같다
!= 다르다
> 크다
< 작다
>= 크거나 같다
<= 작거나 같다
 

논리 연산자

연산자 설명
&& AND (그리고)
`  
! NOT (부정)
 

복합 대입 연산자

x += 3;  // x = x + 3;
x *= 2;  // x = x * 2;

증감 연산자

i++;   // 후위 증가
++i;   // 전위 증가

🧠 연산자 우선순위

연산자는 실행 순서가 정해져 있으며, 괄호로 우선순위를 조절할 수 있다.

  1. 괄호 ()
  2. 단항 연산자 (++, --, !)
  3. 산술 연산자 (*, /, %, +, -)
  4. 비교 연산자 (<, >, ==, !=)
  5. 논리 연산자 (&&, ||)
  6. 대입 연산자 (=, +=, -=, ...)

✍️ 문자열 처리 기초

C#의 문자열은 string 자료형이며 다양한 기능을 지원한다.

문자열 연결

string fullName = "Hello" + " " + "World";

문자열 분할

string sentence = "apple,banana,orange";
string[] fruits = sentence.Split(',');

문자열 검색

int index = "Hello World".IndexOf("World");  // 6

문자열 대체

string message = "I like Java";
string updated = message.Replace("Java", "C#");

문자열 변환

int num = int.Parse("123");
string text = num.ToString();

문자열 비교

string a = "Apple";
string b = "apple";
bool isEqual = a == b; // false (대소문자 구분)
int result = string.Compare(a, b); // 사전 순 비교

문자열 보간 (Interpolation)

string name = "Kero";
int age = 30;
string result = $"My name is {name} and I am {age} years old.";

📣 학습 태도와 조언

형변환과 입력은 사용자와 프로그램이 데이터를 주고받는 가장 기초적인 단계이다.
암시적 변환과 명시적 변환의 차이를 명확히 이해하고, 모든 입력은 문자열로 들어온다는 점을 항상 염두에 두어야 한다.
또한, 다양한 연산자와 문자열 처리 기능을 실제로 코딩해보며 손에 익히는 것이 중요하다.
문법은 암기보다 사용 경험이 우선이며, 직접 작성하고 테스트하면서 익숙해지는 것이 가장 좋은 학습 방법이다.

반응형