C# 기초 정리 2 - 메서드와 구조체

woollim·2024년 9월 22일
0

C#

목록 보기
2/11

1. Method

○ 메서드란?

  • 일련의 코드 블록으로, 특정한 작업을 수행하기 위해 사용되는 독립적인 기능 단위
  • 코드의 재사용성과 모듈화를 위해 사용되며, 필요할 때 호출하여 실행

○ 메서드의 역할과 중요성

1) 코드의 재사용성
: 메서드를 사용하면 동일한 작업을 반복해서 구현하지 않아도 됨. 필요할 때 메서드를 호출하여 작업 수행 가능

2) 모듈화
: 메서드를 사용하여 코드를 작은 단위로 분리하고 관리. 각 메서드는 특정한 기능을 수행하므로 코드의 구조가 더욱 명확해짐

3) 가독성과 유지보수성
: 메서드를 사용하면 코드가 간결해지고 가독성이 향상 됨. 또한, 코드 수정이 필요한 경우 해당 메서드만 수정하면 되므로 유지보수가 용이

4) 코드의 중복 제거
: 반복적인 작업을 메서드로 묶어서 사용하면 코드 중복을 방지

5) 코드의 추상화
: 메서드를 통해 작업 단위를 추상화하고, 메서드 이름을 통해 해당 작업이 어떤 역할을 하는지 파악 가능


○ 메서드 기본형태

  • 반환 값이 있는 메서드
  • 접근제한자 반환형 메서드이름(매개변수) { 메서드 코드 }
public int Sum(int a, int b)
{
    return a + b;
}
  • 반환 값이 없는 메서드
  • 접근제한자 void 메서드이름(매개변수) { 메서드 코드 }
public void PrintHello()
{
    Console.WriteLine("Hello");
} // 반환 값이 없을 때는 void

1) 접근제한자(Access Modifier) : 메서드에 접근할 수 있는 범위를 지정함. public, private, protected 등

2) 리턴 타입(Return Type)
: 메서드가 반환하는 값의 데이터 타입 지정. 반환 값이 없을 경우 void를 사용

3) 메서드 이름(Method Name)
: 메서드를 호출하기 위해 사용하는 이름

4) 매개변수(parameters)
: 메서드에 전달되는 입력 값. 필요한 경우 0개 이상의 매게 변수를 정의할 수 있음

5) 메서드 실행에 코드(Method Body)
: 중괄호 { } 안에 메서드가 수행할 작업을 구현


○ 메서드 오버로딩

  • 메서드 오버로딩은 동일한 이름의 메서드를 다양한 매개변수 목록으로 다중 정의하는 개념
  • 매개변수의 개수, 타입, 순서가 다른 여러 메서드를 동일한 이름으로 정의하여 메서드 호출시 매개 변수의 형태에 따라 적절한 메서드가 선택되도록 함
  • 매개변수만 중요. 반환값만 다른건 아무의미 없음
namespace Study_C_
{
    internal class Program
    {
        static int AddNumbers(int a, int b)
        {
            return (a + b);
        }
        
        static float AddNumbers(float a, float b)
        {
            return (a + b);
        }

        static int AddNumbers(int a, int b, int c)
        {
            return a + b + c;
        }
        
        static void Main(string[] args)
        {
            // 오버로딩 실습
            int sum1 = AddNumbers(10, 20);
            float sum2 = AddNumbers(10.5f, 21.5f);
            int sum3 = AddNumbers(10, 20, 30);        
        
        }
    }
}


2. 구조체

○ 구조체란?

  • 여러 개의 데이터를 묶어서 하나의 사용자 정의 형식으로 만들기 위한 방법
  • 구조체는 값 형식(Value Type)으로 분류되며, 데이터를 저장하고 필요한 기능을 제공함
  • 구조체는 struct 키워드를 사용하여 선언
  • 구조체의 멤버는 변수와 메서드로 구성
struct Person
{
    public string Name;
    public int Age;
    
    public void PrintInfo()
    {
        Console.WriteLine("Name : {0}, Age : {1}", Name, Age);
    }
}

○ 구조체 사용

  • 구조체는 변수를 선언하여 사용할 수 있음
  • 구조체의 멤버에는 접근할 때 ' . ' 연산자를 사용
  • 구조체의 멤버 기본 접근지정자는 private
  • 구조체를 사용할 때 new 키워드를 사용하여 인스턴스를 초기화
  • 구조체는 값 타입(value type), 클래스와 힙(heap)에 할당되지 않고 스택(stack)에 할당 됨
Person person1 = new Person();
person1.Name = "John";
person1.Age = 25;
person1.PrintInfo();    // Name : John, Age : 25

○ 구조체의 new

  • C#에서 구조체는 값 타입이기 때문에, new 키워드를 사용하지 않더라도 인스턴스를 생성할 수 있음.
struct MyStruct
{
    public int Value;
}

// new 없이 생성
MyStruct struct1;
struct1.Value = 5; // 필드를 사용하기 전에 초기화해야 함

// 기본값으로 생성
MyStruct struct2 = new MyStruct(); // struct2.Value는 0
  • new를 사용하지 않으면 구조체의 필드를 사용하기 전에 반드시 값을 설정해야 하며, 그렇지 않으면 컴파일 오류가 발생할 수 있음
  • new를 사용하면 모든 필드가 기본값으로 초기화된 인스턴스가 생성됨. 를 들어, 숫자 타입은 0, 불리언은 false, 참조형은 null로 초기화


3. 메서드와 구조체의 차이

○ 메서드

  • 기능 : 어떤 작업을 수행하는 코드 블록
  • 역할 : 특정 동작(연산, 처리 등)을 수행하고, 필요하면 값을 반환
  • 선언 : 클래스나 구조체 내에서 선언되며, 호출하여 실행
void PrintMessage()
{
    Console.WriteLine("Hello, World!");
}

○ 구조체

  • 기능 : 데이터(필드, 속성)를 담는 틀
  • 역할 : 여러 데이터(변수)나 관련 정보를 하나의 단위로 묶어 관리
  • 선언 : 값 타입으로, 클래스와 유사하게 필드, 속성, 메서드 등을 가질 수 있음
struct Point
{
    public int X;
    public int Y;
}

○ 차이점 요약

  • 메서드는 작업을 수행하고, 구조체는 데이터를 저장함
  • 메서드는 동작(기능)에 집중하고, 구조체는 여러 데이터를 하나로 묶는 용도로 사용됨


4. C++와 C#의 메서드, 구조체 차이

○ 메서드

  • C++ 메서드
    • 가상 함수 : C++에서는 가상 함수를 통해 다형성(polymorphism)을 구현할 수 있음. 함수 오버라이딩을 통해 부모 클래스의 메서드를 자식 클래스에서 재정의할 수 있음
    • 구조체 내 메서드 : 구조체도 클래스처럼 메서드를 가질 수 있음. 구조체와 클래스의 주요 차이는 기본 접근 지정자뿐이므로 구조체 내 메서드는 클래스와 거의 동일하게 작동함. 구조체는 public, 클래스는 private
    • 디폴트 인자 : C++에서는 메서드에 디폴트 인자를 제공할 수 있어, 메서드를 호출할 때 일부 인자를 생략할 수 있음
    • 오버로딩 : C++에서는 같은 이름의 메서드를 여러 개 정의할 수 있습니다(오버로딩)
class MyClass
{
public:
    virtual void Display() // 가상함수 처리할 부모의 함수
    {
        std::cout << "Base Class" << std::endl;
    }
};

class Derived : public MyClass
{
public:
    void Display() override // 오버라이딩할 자식의 함수
    {
        std::cout << "Derived Class" << std::endl;
    }
};
  • C# 메서드
    • 가상 메서드 : C#에서도 virtual 키워드를 사용해 가상 메서드를 정의하고, override 키워드로 자식 클래스에서 재정의할 수 있음
    • 구조체 내 메서드 : C# 구조체도 메서드를 가질 수 있지만, 상속 및 가상 메서드를 활용할 수 없음
    • 디폴트 인자 : C#도 C++처럼 메서드에 디폴트 인자를 제공할 수 있지만, 일부 제한이 있음
    • 오버로딩 : C#에서도 메서드 오버로딩이 가능

  • 차이 요약
    • 두 언어 모두 가상 함수, 오버로딩을 지원하지만, C++이 더 유연한 메모리 관리와 디폴트 인자 등을 제공
    • C#에서는 메서드 오버라이딩 시 반드시 override 키워드를 명시해야 함

○ 구조체

  • C++ 구조체
    • 기본 접근 지정자 : 구조체의 기본 접근 지정자가 public
    • 상속 : C++에서는 구조체도 상속이 가능. 다만, 기본 접근 지정자가 public일 뿐 클래스와 구조적으로는 차이가 거의 없음
    • 메모리 할당 : 구조체는 스택이나 힙에 모두 할당 가능. 동적으로 할당할 경우 new 키워드를 사용할 수 있음
    • 기본 값 타입 : 구조체는 값 타입이 아닌 참조 타입(reference type)처럼 동작할 수 있음
    • 메서드 포함 : C++ 구조체는 멤버 함수(메서드)를 포함할 수 있으며,
      가상 함수도 정의 가능
struct MyStruct
{
    int x;
    void Display()
    {
        std::cout << x << std::endl;
    }
};
  • C# 구조체
    • 기본 접근 지정자 : C#에서는 구조체의 기본 접근 지정자가 private
    • 상속 : C#의 구조체는 상속이 불가능. 구조체는 클래스로부터 상속받을 수 없고, 상속할 수도 없음
    • 메모리 할당 : C# 구조체는 기본적으로 스택에 할당되며, new 키워드를 사용할 경우도 스택에 할당 됨. 힙에 할당되는 경우는 박싱(Boxing)이 일어남 박싱 설명 링크
    • 값 타입 : C# 구조체는 값 타입. 이는 할당 시 복사가 이루어지며 참조가 아닌 값 자체가 전달 됨
    • 메서드 포함 : C# 구조체도 메서드를 포함할 수 있지만, C++처럼 가상 함수나 상속을 활용할 수 없음
struct MyStruct
{
    public int x;
    public void Display()
    {
        Console.WriteLine(x);
    }
}
  • 차이 요약
    • C++에서는 구조체의 기본 접근 지정자가 public, C#에서는 private
    • C++ 구조체는 클래스처럼 상속과 가상 함수 등을 사용할 수 있지만, C#에서는 불가능
    • C++ 구조체는 참조 타입처럼 사용할 수 있고, C# 구조체는 값 타입


5. C++와 C# 차이 심화 정리

  1. C# 메서드 디폴트 인자 제한점
  • 메서드 시그니처 충돌
    • 메서드 오버로딩을 사용할 때, 디폴트 인자와의 충돌을 주의해야 함. 만약 디폴트 인자를 설정한 메서드가 있고, 동일한 시그니처로 오버로딩된 메서드가 있으면 컴파일 오류가 발생할 수 있음
    • C++에서는 이러한 경우 디폴트 인자를 사용하는 메서드와 별도로 오버로딩된 메서드를 만들 수 있지만, C#에서는 허용되지 않음
public class MyClass
{
    public void Greet(string name = "Guest")
    {
        Console.WriteLine($"Hello {name}");
    }

    // 오류 발생: 디폴트 인자가 있는 메서드와 동일한 시그니처로 인해 충돌
    // public void Greet()
    // {
    //     Console.WriteLine("Hello Guest");
    // }
}
  • 디폴트 인자는 컴파일 타임에 결정
    • C#에서 디폴트 인자의 값은 컴파일 타임에 결정 됨. 즉, 기본값이 참조형 변수거나 외부의 동적 값일 경우, 이 값이 변해도 디폴트 인자는 변하지 않음
    • 디폴트 값은 컴파일 시 결정되므로, 런타임에 변경할 수 없음. 예를 들어, 변수를 이용해 동적으로 디폴트 인자를 변경하는 것은 불가능
public class MyClass
{
    const int DefaultAge = 25;  // 상수로 선언된 기본값

    public void Greet(int age = DefaultAge)
    {
        Console.WriteLine($"Age: {age}");
    }
}
  • 선택적 인자를 읽는 순은 왼쪽에서 오른쪽으로만
    • 디폴트 인자를 제공하는 경우, 인자를 생략할 때는 오른쪽부터 왼쪽 순서대로 생략할 수 있음. 즉, 중간에 있는 인자를 생략하고 마지막 인자를 제공하는 것은 불가능
public void Greet(string name = "Guest", int age = 25)
{
    Console.WriteLine($"Hello {name}, age {age}.");
}

// 호출
Greet(age: 30);  // 오류: 중간 인자를 생략하고 마지막 인자를 지정하는 것은 불가능

2. C++에서 구조체 상속과 가상 함수 사용하기
#include <iostream>
using namespace std;

// Base 구조체
struct Animal 
{
    string name;

    // 가상 함수: 파생 구조체에서 재정의 가능
    virtual void Speak() 
    {
        cout << name << " is making a sound." << endl;
    }

    // 가상 소멸자: 동적 할당된 객체를 안전하게 소멸시키기 위해 필요
    virtual ~Animal() {}
};

// Derived 구조체 (Animal을 상속)
struct Dog : public Animal 
{
    Dog(const string& dogName) 
    {
        name = dogName;
    }

    // 가상 함수 재정의 (override)
    void Speak() override 
    {
        cout << name << " says Woof!" << endl;
    }
};

// 또 다른 파생 구조체 (Animal을 상속)
struct Cat : public Animal 
{
    Cat(const string& catName) 
    {
        name = catName;
    }

    // 가상 함수 재정의 (override)
    void Speak() override 
    {
        cout << name << " says Meow!" << endl;
    }
};

int main() 
{
    // Animal 포인터를 사용하여 Dog 및 Cat 객체를 가리킴
    Animal* myDog = new Dog("Buddy");
    Animal* myCat = new Cat("Whiskers");

    // 가상 함수를 통해 다형성 구현
    myDog->Speak();  // Buddy says Woof!
    myCat->Speak();  // Whiskers says Meow!

    // 메모리 해제
    delete myDog;
    delete myCat;

    return 0;
}

1) 상속

  • Animal 구조체는 Dog와 Cat 구조체의 기반(Base) 구조체. 두 구조체는 Animal로부터 상속받음
    상속 시, 구조체의 기본 접근 지정자가 public이므로 명시적으로 public 상속을 지정함

2) 가상 함수

  • Animal 구조체의 Speak() 메서드는 가상 함수(virtual function)로 선언 됨. 이로 인해 파생된 구조체(Dog, Cat)에서 Speak()를 재정의할 수 있음
    Dog와 Cat 구조체는 각자 Speak() 메서드를 재정의(override)하여 각각 다른 동작 함

3) 다형성(Polymorphism)

  • Animal* 타입의 포인터로 Dog와 Cat 객체를 가리킬 수 있음. 이때 가상 함수 덕분에 올바른 Speak() 함수가 호출 됨
    myDog->Speak()는 Dog의 Speak()가 호출되고, myCat->Speak()는 Cat의 Speak()가 호출 됨

4) 가상 소멸자

  • Animal 구조체의 소멸자도 가상 소멸자로 선언 됨. 이는 delete로 객체를 해제할 때, 올바른 파생 구조체의 소멸자가 호출되도록 보장 함

5) 실행 결과

Buddy says Woof!
Whiskers says Meow!

0개의 댓글