메소드 구조 및 정의
C# 언어에서 메소드(Method)는 클래스나 구조체 내에 정의되며, 특정 작업을 수행하는 코드 블록이다. 객체의 행위(Behavior)를 정의하는 핵심 요소이다. 메소드의 구조 및 정의는 다음과 같다.
[접근 한정자] [수식어] [반환 타입] [메소드 이름]([매개 변수 리스트])
{
// 실행할 코드 블록
[return 반환 값;] // void 반환 타입일 때 생략 가능
}
구성 요소에 대한 표는 다음과 같다.
| 구성 요소 | 설명 |
|---|---|
| 접근 한정자 | 메소드에 접근할 수 있는 범위를 지정한다. |
| 수식어 | 메소드의 특성을 지정한다. |
| 반환 타입 | 메소드가 작업을 마치고 반환하는 값의 타입이다. 반환 값이 없으면 void 타입을 사용한다. |
| 메소드 이름 | 메소드를 호출할 때 사용하는 이름이며, 카멜 표기법(Camel Case)을 권장한다. |
| 매개 변수 리스트 | 메소드가 외부로부터 전달받는 값들이다. |
메소드 매개 변수 전달 방식
C# 언어는 매개 변수를 전달하는 네 가지 주요 방식을 제공하며, 이는 메소드 호출 시 메모리 작동 방식에 영향을 준다.
| 전달 방식 | 키워드 | 동작 원리 | 특징 |
|---|---|---|---|
| 값에 의한 전달 | 없음 | 값이 복사되어 전달된다. 메소드 내부에서 값을 변경해도 원본 변수에는 영향이 없다. | 기본 동작 방식이다. |
| 참조에 의한 전달 | ref | 변수의 메모리 주소가 전달된다. 메소드 내부에서 값을 변경하면 원본 변수의 값이 변경된다. | 호출 전 원본 변수가 반드시 초기화되어야 한다. |
| 출력 매개 변수 | out | 참조에 의한 전달과 유사하지만, 메소드가 반드시 값을 할당해야 한다. | 호출 전 원본 변수의 초기화가 필수적이지 않다. |
| 입력 참조 | in | 참조를 전달하지만, 메소드 내부에서 값을 읽기 전용(Read-Only)으로만 사용해야 한다. | 대형 구조체 전달 시 복사 오버헤드를 줄인다. |
- 선택적 인수(Optional Arguments): 메소드를 정의할 때 매개변수에 기본값을 할당하여, 호출자가 해당 인수를 생략하고 호출할 수 있게 한다.
public void Greet(string name, string greeting = "안녕하세요")
{
Console.WriteLine($"{name}님, {greeting}!");
}
// 호출 시:
Greet("김철수"); // "김철수님, 안녕하세요!"
Greet("이영희", "반가워"); // "이영희님, 반가워!"
- 명명적 인수(Named Arguments): 소드를 호출할 때 매개변수의 이름과 값을 명시적으로 지정하는 방식으로, 인수의 순서에 상관없이 값을 전달할 수 있으며, 코드를 읽는 사람이 인수의 용도를 명확히 이해할 수 있다.
public void PrintInfo(string name, int age, string city) { /* ... */ }
// 명명된 인수를 사용한 호출 (순서 변경 가능)
PrintInfo(age: 30, city: "서울", name: "박민수");
특수한 형태의 메소드
다음과 같이 메소드는 특수한 형태가 존재한다.
-
정적 메소드(Static Method): 특정 객체의 인스턴스가 아닌 클래스 자체에 속하는 메소드로, 인스턴스 없이
클래스명.메소드명()으로 호출한다. 단 static 메소드 내에서는 인스턴스 필드나 인스턴스 메소드에 직접 접근할 수 없다. -
확장 메소드(Extension Method): 기존 클래스를 수정하지 않고도 해당 클래스에 새 메소드를 추가할 수 있는 기능으로, static 클래스 내에서 static 메소드로 정의하며, 첫 번째 매개 변수 앞에 this 키워드를 붙여 확장할 타입을 지정한다.
-
오버로딩(Overloading): 같은 이름의 메소드를 여러 개 정의하는 기능으로, 매개 변수의 개수나 타입이 달라야 하며, 반환 타입만 다를 경우에는 오버로딩이 성립하지 않는다.
-
오버라이딩(Overriding): 상속 관계에서 부모 클래스의 가상 메소드(Virtual Method)를 자식 클래스에서 재정의하는 기능으로, 다형성을 구현하는 핵심이다.
-
재귀 호출(Recursive Call): 재귀 호출은 메소드 내 자기 자신을 다시 호출하는 방식으로, 문제를 더 작고 단순한 형태로 쪼개어 해결할 때 유용하다. 단 재귀 호출을 멈추는 종료 조건이 반드시 명확해야 하며, 조건이 없으면 스택 오버플로우가 발생한다.
람다 표현식 메소드
메소드의 본문이 단일 표현식으로 이루어져 있다면, 코드의 간결성을 위해 람다 표현식(=>) 문법을 사용하여 정의할 수 있다. 예제 코드는 다음과 같다.
// 일반 메소드
public int Add(int a, int b)
{
return a + b;
}
// 람다 표현식 메소드(Expression-Bodied Method)
public int Add(int a, int b) => a + b;