메서드(Method)와 매개 변수(Parameter)
C#에서는 모든 함수가 클래스 내에 선언되기에 함수 대신에 메서드(method)라 부릅니다. 메서드는 클래스가 수행할 수 있는 기능들을 하나의 이름으로 묶어 관리하는 코드 블록을 말합니다. 여기서는 클래스의 구성 요소인 메서드에 대해 좀 더 자세히 살펴보겠습니다.
> // 메서드: 클래스가 수행할 수 있는 기능들을 하나의 이름으로 묶어 관리하는 코드 블록
메서드 소개
이미 우리는 메서드에 대해서 많은 연습을 한 상태이기에 메서드에 대해서 다음과 같이 간단히 정리하고 넘어가겠습니다.
- 클래스 내에서 선언된 함수(Function, Sub Procedure)를 메서드라 합니다.
- 특정한 코드를 묶어서 실행하고자 할 때 사용하는 코드 블록이 메서드입니다.
- 메서드는 개체가 수행할 수 있는 기능/동작/행위 등을 말합니다.
- 자동차Car의 동작/기능인 전진, 후진, 좌회전과 같은 의미의 기능을 구현할 때 사용됩니다.
- 메서드 이름은 다음과 같이 동사+명사의 형태를 권장합니다.
GetPrice()
SetPrice()
메서드 선언 모양
Car
클래스에 달리는 동작을 의미하는 Go
메서드를 만드는 모양은 다음과 같습니다.
> class Car
. {
. public static void Go()
. {
. // Go 메서드의 기능 정의
. }
. }
- 다른 클래스에서
Car
클래스의Go
메서드에 접근하게 하기 위해서는public
액세스 한정자를 사용합니다. static
키워드를 붙이면 정적 호출이 가능하여Car.Go()
형태로 호출이 됩니다.void
키워드는Go
메서드의 결괏값(반환값, return value)이 없다는 걸 의미합니다.Go
메서드의 괄호 안에는Go
메서드에서 필요한 매개 변수들을 지정할 수 있습니다.
메서드 호출 모양
메서드 선언 구문으로 만들어진 메서드는 괄호 기호를 포함하여 호출할 수 있습니다.
Car
클래스의 정적인 메서드인 Go
메서드는 다음과 같이 호출할 수 있습니다.
> class CarGoTest
. {
. static void Main()
. {
. Car.Go();
. }
. }
인스턴스 메서드 호출: 클래스의 인스턴스 멤버 호출
특정 클래스에 static
키워드가 빠진 메서드를 만들면 이는 인스턴스 개체를 통해서 호출할 수 있습니다.
코드: Rectangle.cs, RectangleClass.cs
> public class Rectangle
. {
. public string GetName()
. {
. return "직사각형";
. }
. }
>
> // Rectangle 클래스의 인스턴스를 생성
> Rectangle rectangle = new Rectangle();
> rectangle.GetName()
"직사각형"
public
메서드와 private
메서드 사용하기
메서드의 액세스 한정자에는 주로 public
과 private
이 사용됩니다. 제한없이 접근하게 할 때에는 public
을 붙이고 해당 클래스에서만 접근하게 할 때에는 private
을 붙입니다.
동영상 강의
코드: MethodPrivate.cs
using System;
class Dog
{
public void Eat()
{
Console.WriteLine("[1] 밥을 먹는다.");
this.Digest(); // [2] private 메서드 호출
}
private void Digest()
{
Console.WriteLine("[2] 소화를 시킨다.");
}
}
class MethodPrivate
{
static void Main()
{
Dog dog = new Dog();
dog.Eat(); // [1] public 메서드 호출
}
}
코드: MethodPrivate.java
class Dog {
public void eat() {
System.out.println("[1] 밥을 먹는다.");
this.digest(); // 나 자신의 private 메서드 호출
}
private void digest() {
System.out.println("[2] 소화를 시킨다.");
}
}
public class MethodPrivate {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
}
}
[1] 밥을 먹는다.
[2] 소화를 시킨다.
[1]
번 코드와 같이 다른 클래스에서 호출이되는 메서드를 제작할 때에는 public
액세스 한정자를 붙입니다. [2]
번 코드와 같이 private
액세스 한정자가 붙은 메서드는 해당 클래스 내에서만 호출이되고 다른 외부 클래스에서는 접근이 불가능한 메서드가 됩니다.
메서드의 매개 변수 전달 방식의 종류
메서드의 매개 변수는 사용되는 방식에 따라서 4가지로 분류됩니다. 지금까지 기본으로 사용한 매개 변수 전달 방식은 값 전달 방식입니다. 이에 추가해서 ref
키워드를 사용하는 참조 전달 방식과 out
키워드를 사용하는 반환형 전달 방식 그리고 마지막으로 params
키워드를 사용하는 가변형 전달 방식이 있습니다. 각각에 대한 내용은 예제로 따로 살펴보겠지만 간단하게 4가지 매개 변수 전달 방식에 대한 설명은 다음과 같습니다.
- 값 전달 방식: 말 그대로 값을 그대로 복사(Copy)해서 전달하는 방식을 의미합니다. 지금까지 사용해 왔던 매개 변수 방식입니다.
- 참조 전달 방식(
ref
): 실제 데이터는 매개 변수가 선언된 쪽에서만 저장하고 호출된 메서드에서는 참조(가리키는)만 하는 형태로 변수 이름만 전달하는 방식입니다. - 반환형 전달 방식(
out
): 메서드를 호출하는 쪽에서 선언만 하고, 초기화를 하지 않고 전달하면, 메서드쪽에서 해당 데이터를 초기화해서 넘겨주는 경우입니다. - 가변형 전달 방식(
params
): 한 개 이상의 매개 변수를 가변적으로 받을 때 매개 변수 선언시params
키워드를 붙입니다. 가변적이라는 것은 동일 타입으로 하나 이상을 받을 수 있도록 배열형으로 받음을 의미합니다. 가변 길이 매개 변수는 반드시 매개 변수 선언시 마지막에 위치해야 합니다.
메서드의 매개 변수 전달 방식 3가지 비교
메서드의 매개 변수 전달 방식으로 사용되는 3가지 방식을 예제 단위로 비교해서 설명하겠습니다.
값 전달 방식
첫 번째로 매개 변수 전달 방식의 거의 대부분으로 사용되는 값 전달 방식을 정리하겠습니다.
코드: ParameterIn.cs
> int num = 10;
> Console.WriteLine($"[1] {num}"); // [1] 10
[1] 10
>
> static void Do(int num)
. {
. num = 20;
. Console.WriteLine($"[2] {num}"); // [2] 20
. }
>
> Do(num);
[2] 20
>
> Console.WriteLine($"[3] {num}"); // [3] 10
[3] 10
[1]
번 코드에서는 Main
메서드의 지역 변수인 num
의 값인 10이 그대로 출력됩니다.
[2]
번 코드에서는 Main
메서드에서 전달된 num
의 값인 10이 Do
메서드의 num
매개 변수에 저장되고 num
매개 변수의 값에 20을 대입하여 변경한 후 20이 출력이 됩니다. 여기서 20으로 변경된 것은 Do
메서드에서만 이루어졌지 Main
메서드의 num
변수에는 영향을 주지 않습니다.
[3]
번 코드가 실행될 때에는 num
는 Main
메서드의 지역 변수이기에 10인 상태로 그대로 출력됩니다.
참조 전달 방식
참조 전달 방식은 ref
키워드를 사용하여 로컬 변수를 공유하고자 할 때 사용되는 방식입니다.
코드: ParameterRef.cs
using System;
class ParameterRef
{
static void Main()
{
int num = 10;
Console.WriteLine($"[1] {num}"); // [1] 10
Do(ref num); // 참조 전달 방식
Console.WriteLine($"[3] {num}"); // [3] 20
}
static void Do(ref int num)
{
num = 20; // [A] 호출한 부분에 적용(Main 메서드의 num 변수의 값이 변경됨)
Console.WriteLine($"[2] {num}"); // [2] 20
}
}
[1] 10
[2] 20
[3] 20
[1]
번 코드 영역은 지역 변수 num의 값인 10인 출력됩니다.
[2]
번 코드 영역의 매개 변수 num는 Main 메서드의 지역 변수인 num를 참조하고 있기에 Do 메서드에서 값이 변경되면 그 변경되는 값은 Main 메서드의 num에 반영된다.
[3]
번 코드에서는 Main 메서드의 num 지역 변수의 값이 이미 20으로 변경되었기에 20이 출력된다.
out 키워드로 반환형 매개 변수 전달 방식 함수 만들기
메서드를 호출하기 전에는 굳이 지역 변수를 초기화하지 않고 호출한 메서드에서 전달한 값을 받아서 사용해야 하는 경우가 있습니다. 이런 경우에는 out
키워드를 사용하여 반환형 매개 변수 전달 방식을 사용할 수 있습니다.
코드: ParameterOut.cs
//[?] out 키워드로 반환형 매개 변수 전달 방식 함수 만들기
using System;
class ParameterOut
{
static void Do(out int num)
{
num = 1234; // [B] ref와 동일: 호출한 부분에 적용, 반드시 초기화해야 함
Console.WriteLine($"[1] {num}"); // [1] 1234
}
static void Main()
{
int num; // [A] 변수를 반드시 초기화할 필요 없음
Do(out num); // 반환형 매개 변수 전달 방식
Console.WriteLine($"[2] {num}"); // [2] 1234
}
}
[1] 1234
[2] 1234
[A]
번 코드처럼 지역 변수의 값을 초기화하지 않고 특정 메서드에서 초기화하는 형태가 있습니다. 이런 경우에는 ref
를 사용해도 되지만 out
키워드를 사용할 수가 있습니다.
[B]
번 코드에서 1234로 초기화하면 ref
와 동일하게 Main
메서드의 num
지역 변수가 1234로 초기화 됩니다.
[1]
번 코드에서는 Do
메서드에서 초기화된 1234가 출력되고, [2]
번 코드영역에서는 Main
메서드의 num
변수가 1234로 이미 초기화되었기에 1234가 출력됩니다.
이처럼 out
키워드를 사용하는 방식은 ref
와 동일하지만, 어차피 특정 메서드에서 초기화할 경우라면 전달할 때 초기화하지 않고 전달해도 됩니다.
NOTE
학습하는데 조금의 위안을 드린다면, ref
와 out
키워드를 직접 사용하여 메서드를 만들 경우는 초반에는 극히 드뭅니다. 만약, .NET 프레임워크와 같이 프레임워크 레벨의 개발로 들어가기 전까지는 이미 .NET에 잘 만들어 있는 내장 메서드들을 우선 잘 사용하면 충분합니다. 시간이 지나면 ref
와 out
은 어렵지 않게 받아드려집니다.
Pass by Value와 Pass by Reference의 차이점
날짜 형태의 문자열을 날짜형으로 변환하기: out
키워드 사용
이번에는 날짜 형태의 문자열을 진짜 날짜형으로 변환하는 방법을 알아보겠습니다. 모든 기본 형식들이 가지고 있는 TryParse()
메서드를 사용한 예제인데요. TryParse()
메서드는 앞으로 여러 번 연습할 테니 이번 예제에서는 문자열을 날짜형으로 변환하는 내용에 초점을 맞추면 됩니다.
문법:
자료형.TryParse("변환할 내용", out "변환이 되면 담을 그릇")
코드: DateTimeTryParse.cs
// 자료형.TryParse("변환할 내용", out "변환이 되면 담을 그릇")
using System;
class DateTimeTryParse
{
static void Main()
{
DateTime dt; // [1] 반환형 매개 변수에 사용될 지역 변수(초기화하지 않음)
// [2] DateTime.TryParse()로 날짜 형식으로 변환 시도(변환 가능하면 dt에 저장)
if (DateTime.TryParse("2023-01-01", out dt))
{
Console.WriteLine(dt);
}
else
{
Console.WriteLine("날짜 형식으로 변환할 수 없습니다.");
}
// [3] TryParse() 메서드에 지역 변수 선언과 동시에 초기화 가능
if (DateTime.TryParse("2023-01-01", out var myDate))
{
Console.WriteLine(myDate);
}
}
}
2023-01-01 오전 12:00:00
2023-01-01 오전 12:00:00
[1]
번에서 날짜 형식으로 변환되면 담을 그릇을 초기화하지 않고 선언할 수 있습니다.
[2]
번 코드 영역에서 TryParse()
메서드에 의해서 날짜 형식으로 변환이 되면 out
키워드로 지정된 dt
변수에 변환된 날짜 값이 저장됩니다.
[3]
번 코드 영역은 out var
형태를 사용하여 [1]
번과 [2]
번 코드를 줄여서 표현하는 방법입니다.
가변 길이 매개 변수
메서드의 매개 변수를 받을 때 하나의 배열 형식의 매개 변수를 사용하여 가변적으로 하나 이상의 값을 받아서 배열에 저장하여 사용할 수 있는 가변형 매개 변수를 제공합니다. 가변형 매개 변수는 params
키워드를 사용하여 배열형 매개 변수를 선언하면 됩니다. 메서드 오버로드와 달리 하나의 매개 변수로 하나 이상의 값을 받을 수 있는 구조입니다. 이러한 가변 길이 매개 변수를 다른 말로 나머지 매개 변수(Rest Parameter)로도 표현합니다.
params
키워드를 사용하여 가변 길이 매개변수를 갖는 메서드 만들기
이번에는 가변형 매개 변수를 사용해보겠습니다.
예제: 가변 길이 매개 변수로 하나 이상의 매개 변수 받기
코드: ParamsDemo.cs
using System;
class ParamsDemo
{
static void Main()
{
// 가변 길이 매개 변수 => params
Console.WriteLine(SumAll(3, 5)); // 8
Console.WriteLine(SumAll(3, 5, 7)); // 15
Console.WriteLine(SumAll(3, 5, 7, 9)); // 24
}
/// <summary>
/// SumAll() 메서드는 정수 형식의 값을 가변 형식으로 받을 수 있음
/// </summary>
/// <param name="numbers">정수 배열</param>
/// <returns>정수 배열의 합계</returns>
static int SumAll(params int[] numbers) // [1] params로 여러 개의 매개 변수 받기
{
int sum = 0;
foreach (int num in numbers)
{
sum += num;
}
return sum;
}
}
8
15
24
SumAll()
함수는 numbers
라는 배열 형식의 매개 변수 하나를 갖습니다. 이때 params
키워드를 매개 변수 앞에 붙인 것을 볼 수 있습니다. 이 키워드를 붙이면 이 매개 변수가 가변 길이 매개 변수가 됩니다. 가변 길이 매개 변수가 적용된 메서드를 호출할 때에는 SumAll(3, 5)
, SumAll(3, 5, 7)
, SumAll(3, 5, 7, 9)
형태로 하나 이상의 값을 가변적으로 호출할 수 있습니다. 하나의 함수로 여러 개의 동일 패턴의 기능을 구현할 때에는 이처럼 가변 길이 매개 변수 사용이 도움이 됩니다. 참고로 주의할 점은 가변 형식 매개 변수를 일반적인 매개 변수와 함께 사용할 때에는 반드시 마지막에 위치해야 합니다.
3.1.1. ParamsDemo.py
코드: ParamsDemo.py
>>> # ParamsDemo.py
... # sum_all() 함수는 정수 형식의 값을 가변 형식으로 받을 수 있음
... def sum_all(*numbers):
... sum = 0
... for num in numbers:
... sum = sum + num
... return sum
...
... # 가변 길이 매개 변수를 갖는 함수 호출하기
>>> sum_all(3, 5) # 8
8
>>> sum_all(3, 5, 7) # 15
15
>>> sum_all(3, 5, 7, 9) # 24
24
문자열 배열을 받는 가변 길이 매개 변수 사용하기
가변 길이 매개 변수에 대한 또 다른 예제를 살펴보겠습니다. 매개 변수 선언시 params
키워드를 사용하여 배열 형식으로 만들면 하나 이상의 매개 변수를 받을 수 있는 가변 길이 매개 변수가 됩니다.
코드: MultipleParams.cs
> static void Multi(params string[] messages)
. {
. foreach (string message in messages)
. {
. Console.Write(message);
. }
. Console.WriteLine();
. }
>
> Multi("A");
A
> Multi("A", "B");
AB
> Multi("A", "B", "C");
ABC
Multi
메서드는 (params string[] messages)
형태로 매개 변수가 선언되어 있습니다. 이렇게 params
키워드가 지정된 배열 형식의 매개 변수는 가변 길이 매개 변수로 Multi("A")
, Multi("A", "B")
, Multi("A", "B", "C")
형태로 원하는 만큼의 문자열을 배열형으로 받을 수 있습니다. 동일 데이터 형식을 여러 번 입력 받고자할 때에는 params
키워드를 사용하는 가변 길이 매개 변수 방식이 도움이 됩니다.
메서드 본문을 줄여서 표현하기
함수를 공부할 때 이미 우리는 화살표 연산자를 사용하여 함수의 본문을 줄여서 표현하는 방법을 사용했습니다. C# 6.0 버전 이후로는 메서드 본문을 줄여서 표현할 수 있습니다. 이를 식 본문 메서드(Expression Bodied Method, Expression Bodied Function)라고 표현합니다.
식 본문 메서드 사용하기
식 본문 메서드는 우리가 앞서 여러 번 사용해 본 모양입니다. 화살표 기호를 사용하여 메서드를 축약해서 사용할 수 있습니다.
코드: ExpressionBodiedFunction.cs
> static void Work()
. {
. Console.WriteLine("Work"); // [1] 기본 형식
. }
> Work();
Work
>
> static void Walk() => Console.WriteLine("Walk"); // [2] 축약 형식
> Walk();
Walk
[1]
번 코드와 같은 형태는 지금껏 우리가 메서드를 만들 때 사용해 오던 방식입니다. 이 중에서 단일 형태의 출력 또는 반환이 있는 경우에는 [2]
번 코드 형태와 같이 =>
연산자를 사용하여 메서드의 내용을 축약해서 표현할 수 있습니다. 이런 코드 형태는 반드시 할 필요는 없지만 코드를 간결하게 표현할 수 있는 유용한 기능입니다.
단일 표현식의 메서드를 한 줄로 정의하기
결괏값이 하나인 단일 표현식(Single Expression)일 때에는 화살표 연산자인 =>
기호를 사용하여 메서드의 본문을 줄여서 표현할 수 있습니다.
코드: SingleExpression.cs
using System;
class SingleExpression
{
static int AddAge(int age) => age + 1; // return age + 1의 축약 형식
static void Main() => Console.WriteLine(AddAge(100)); // 101
}
101
AddAge()
메서드는 넘어온 정수형 매개 변수의 값을 1증가시킨 후 그 값을 반환해주는 간단한 형태입니다. 이러한 경우에도 화살표 기호를 사용하여 return
키워드를 생략하여 표현할 수 있습니다.
메서드 본문을 줄여서 표현하기
메서드 본문을 줄여서 표현하는 방법인 식 본문 메서드(Expression Bodied Method)를 한번 더 사용해보겠습니다.
코드: ExpressionBodiedMethodDemo.cs
> //[?] Expression Bodied Methods: C# 6.0의 새로운 기능
> // Expression Bodied Methods: => Lambda Operator 사용
> static void Hello() => Console.WriteLine("Hello.");
> static int DoubleValue(int val) => val * 2;
> static int Sum(int a, int b) => a + b;
>
> Hello(); // Hello.
Hello.
> Console.WriteLine(DoubleValue(4)); // 8
8
> Console.WriteLine(Sum(3, 5)); // 8
8
메서드의 본문을 줄여서 표현하는 것은 처음에는 이해가 어려울 수 있지만, 익숙해지면 굉장히 편하게 코드를 작성할 수 있는 장점을 가집니다.
다음 샘플 코드는 특정 클래스의 메서드와 Main()
메서드를 모두 줄여서 표현하였습니다.
코드: GreetingPage.cs
using System;
class Greeting
{
private string message = "사이트에 오신 걸 환영합니다.";
public void Say() => Console.WriteLine(this.message);
}
class GreetingPage
{
static void Main() => (new Greeting()).Say();
}
사이트에 오신 걸 환영합니다.
선택적 매개 변수(Optional Parameter)
메서드의 매개 변수를 선언할 때에는 기본값을 줄 수 있습니다. 이를 선택적 매개 변수(Optional Parameter) 또는 기본 인수(Default Argument)라고 합니다. 이번에는 선택적 매개 변수를 사용해보겠습니다.
코드: OptionalParameterDemo.cs
> //[?] 선택적 매개 변수(Optional Parameter)
> static int Add(int a, int b = 1)
. {
. return a + b;
. }
>
> Add(5)
6
> Add(5, 3)
8
코드에서 Add
메서드의 2번째 매개 변수인 b
는 메서드 시그니처 내에서 int b = 1
형태로 1을 기본값으로 설정하고 있습니다. 기본값이 설정된 메서드는 해당 매개 변수를 생략하면 자동으로 기본값으로 초기화됩니다. 만약 새로운 값으로 매개 변수가 전달되면 그 값으로 초기화됩니다.
선택적 매개 변수와 명명된 매개 변수
C# 4.0에서 처음 도입된 개념인 선택적 매개 변수(Optional Parameter)와 명명된 매개 변수(Named Argument 또는 Named Parameter)는 함수 호출할 때 편리함을 제공해 줍니다. 매개 변수를 인수로 혼용해서 부르기도 합니다. 이번에는 옵셔널 매개 변수를 살펴보겠습니다.
코드: OptionalParameter.cs
> //[?] 선택적 매개 변수와 명명된 매개 변수
> static int Sum(int first = 10, int second = 20)
. {
. return first + second;
. }
>
> Console.WriteLine(Sum(3, 5)); // [1] 3 + 5
8
>
> //[A] 선택적 매개 변수(Optional Parameter)
> Console.WriteLine(Sum()); // [2] 10 + 20
30
> Console.WriteLine(Sum(40)); // [3] 40 + 20
60
> Console.WriteLine(Sum(100, 200)); // [4] 100 + 200
300
>
> //[B] 명명된 매개 변수(Named Parameter)
> Console.WriteLine(Sum(first: 5, second: 4)); // [5] 5 + 4
9
> Console.WriteLine(Sum(second: 3, first: 2)); // [6] 2 + 3
5
> Console.WriteLine(Sum(second: 50)); // [7] 10 + 50
60
[1]
번 코드와 [4]
번 코드는 메서드 호출의 기본 값입니다.
[2]
번 코드는 선택적 인수를 사용하여 매개 변수를 전달하지 않을 경우 기본 값을 사용됩니다.
[3]
번 코드는 매개 변수를 하나만 전달할 경우 나머지는 기본 값을 사용합니다.
[5]
번 코드와 [6]
번 코드처럼 매개 변수의 이름과 콜론(:
) 기호를 사용하여 매개 변수에 값을 직접 할당할 수 있고 호출 위치를 변경할 수 있습니다.
[7]
번 코드는 명명된 인수와 선택적 인수를 함께 사용한 모양입니다.
명명된 매개 변수를 사용하여 메서드 오버로드 구현하기
선택적 인수와 명명된 인수를 사용하면 하나의 메서드로 여러 개의 메서드를 오버로드한 효과를가질 수 있습니다.
코드: MethodOverloadNamed.cs
> //[?] 명명된 매개변수를 사용하여 메서드 오버로드 구현하기
> public class Messenger
. {
. public void PrintMessage(string message, string prefix = "", string suffix = "")
. {
. Console.WriteLine($"{prefix}{message}{suffix}");
. }
. }
>
> Messenger messenger = new Messenger();
> messenger.PrintMessage("My"); // [A]
My
> messenger.PrintMessage(prefix: "Oh ", message: "My"); // [B]
Oh My
> messenger.PrintMessage(prefix: "Oh ", message: "My ", suffix: "God"); // [C]
Oh My God
Messenger
클래스에는 하나의 PrintMessage()
메서드만 있지만, 호출하는 입장에서는 [A]
, [B]
, [C]
코드 형태처럼 서로 1~3개의 매개 변수에 값을 전달할 수 있습니다. 메서드 오버로드를 따로 구현하지 않고 하나의 메서드로 메서드 오버로드를 구현한 모양입니다.
장 요약
메서드는 클래스의 구성 요소 중에서 가장 많이 사용됩니다. 메서드는 매개 변수를 사용하여 그 능력을 더 향상시킬 수 있습니다. 메서드의 사용법은 이 정도로 정리하고 속성에 대한 내용을 학습해 나가도록 하겠습니다.