들어가기에 앞서
이때까지는 모든 기능을 Main 함수 안에서만 작성하였다. 하지만 특정 기능을 여러 번 사용해야 하는 상황에서 코드를 반복적으로 작성해야 하니 불편함이 생겼다. 이를 해결하기 위한 기능을 알아보자
미리 정해진 동작을 수행하는 코드 묶음을 말한다.
어떤 처리를 미리 함수로 만들어 두고, 다시 반복적으로 사용하기 위해 만든다.
1) 자주 쓰는 기능을 한 번에 관리하고, 입력 시의 반복을 줄임
2) 특정 기능에 수정이 필요할 때 한 번에 수정할 수 있음
함수의 종류는 크게 4가지가 있다.
- 입력값을 요구하지 않고, 반환값이 없는 함수
- 입력값을 요구하고, 반환값이 없는 함수
- 입력값을 요구하지 않고, 반환값이 있는 함수
- 입력값을 요구하고, 반환값이 있는 함수
이 네 가지를 예시로 각각 확인해보자.
예를 들어 아래와 같이 삼연격를 출력한다고 하자.
Console.WriteLine("찌르기!");
Console.WriteLine("베기!");
Console.WriteLine("때리기!");
삼연격을 사용할 상황이 있을 때마다 위와 같은 세 줄의 코드로 쓰는 것보다, 함수로 쓰면 편리하게 사용할 수 있다. 위의 코드를 함수로 묶은 다음 메인에서 삼연격을 세 번 호출해 보자.
static void TripleShot()
{
Console.WriteLine("찌르기!");
Console.WriteLine("베기!");
Console.WriteLine("때리기!");
}
static void Main(string[] args)
{
TripleShot();
TripleShot();
TripleShot();
}
결과 출력은 다음과 같이 나온다.
위의 삼연격에서 추가적인 요구사항을 받았다고 해 보자.
-상황 1 에서의 삼연격 데미지 : 찌르기 데미지 10, 베기 데미지 20, 때리기 데미지 5
-상황 2 에서의 삼연격 데미지 : 찌르기 데미지 20, 베기 대미지 5, 때리기 데미지 10
이와 같은 상황을 표현하기 위해선, 입력값을 받는 방식으로 코드를 변경해야 한다.
이를 매개변수를 추가하는 방식으로 만들 수 있다.
// 입력값을 받는 부분 - 매개변수를 추가해준다.
static void TripleShot(int firstDamage, int secondDamage, int thirdDamage)
{
Console.WriteLine("찌르기! 데미지 : {0}", firstDamage);
Console.WriteLine("베기! 데미지 : {0}", secondDamage);
Console.WriteLine("때리기! 데미지 : {0}", thirdDamage);
}
해당 함수를 메인에서 호출하고, 상황에 따른 입력값을 넣어주어야 한다.
static void Main(string[] args)
{
TripleShot(10, 20, 5); // 상황 1의 데미지
TripleShot(20, 5, 10); // 상황 2의 데미지
}
결과 출력은 다음과 같이 나온다.
- 매개변수(Parameter)
함수에 추가(입력)할 데이터의 자료형과 변수명
같은 함수에서도 매개변수의 입력이 다름에 따라 다른 처리가 가능
* 또 다른 예시 : 붕어빵 만들기
static void MakeFishCake(string taste)
{
Console.WriteLine("반죽물을 붓습니다.");
Console.WriteLine("{0} 앙금을 넣습니다.", taste);
Console.WriteLine("익을 때까지 기다립니다.");
Console.WriteLine("{0} 붕어빵 완성!!!", taste);
}
static void Main(string[] args)
{
MakeFishCake("팥");
MakeFishCake("슈크림");
MakeFishCake("피자");
}
결과 출력은 다음과 같이 나온다.
앞선 4가지 함수의 종류 중에 반환값이 없는 함수와 반환값이 있는 함수가 있다고 언급했다.
처음에는 이 반환값이라는 게 무엇인지 잘 이해되지 않았다. 우선 용어 정리부터 해 보자.
- 반환형(Return Type) : 함수의 결과(출력) 데이터의 자료형
-반환값이 있는 경우,
함수가 끝나기 전까지 반드시 return으로 반환형에 맞는 데이터를 출력해야 함
함수 진행 중 return을 만나는 경우, 그 즉시 결과 데이터를 반환하고 함수가 종료됨
-반환값이 없는 경우 반환형은 void이며 return은 생략가능
이렇게 용어 정리를 해봤지만 이렇게만 봐도 살짝 감이 오지 않았다.
하지만, 추가로 공부해보고 내용을 정리한 바로 반환값의 유무의 차이를 이렇게 말할 수 있다.
- 결과에 대해서 반환형이 없다는 건 그 함수 자체로 그냥 실행된다는 것
- 결과에 대해서 반환형이 있다는 건 그 함수에서 도출된 값을 활용할 수 있다는 것.
이렇게 정리할 수 있다.
이와 같은 내용을 숙지하고 반환값이 있는 함수에 대해 알아보자
간단하게 pi 값을 반환하는 함수를 만들고, Main에 호출해 보았다.
static double GetPI()
{
double pi = 3.141592;
Console.WriteLine("파이값을 반환하였습니다");
return pi;
}
static void Main(string[] args)
{
double returnDoubleNum;
returnDoubleNum = GetPI();
Console.WriteLine(returnDoubleNum);
}
결과 출력은 다음과 같이 나온다.
반환값이 없는 함수와의 차이는, 이 반환된 값을 활용할 수 있다는 것이다.
간단하게 예시를 들어, 1) 에서 만든 만든 3연격 함수를 어떤 값으로 담아내려 할 때 아래와 같이 오류가 표시된다.
애초에 반환할 값이 없기 때문에 변환할 수가 없다는 오류가 나온다.
하지만 위의 pi를 호출하는 함수는, 반환한 결과를 활용할 수 있다.
static double GetPI()
{
double pi = 3.141592;
Console.WriteLine("파이값을 반환하였습니다");
return pi;
}
static void Main(string[] args)
{
double returnDoubleNum;
returnDoubleNum = GetPI() / 2; // 나누기 2를 해야한다고 가정
Console.WriteLine(returnDoubleNum);
}
결과 출력은 다음과 같이 나온다.
의도한 대로 잘 나온 것을 확인할 수가 있다.
예를 들어, 총 데미지 계산식이 다음과 같다고 가정해보자.
데미지 = 레벨 * 기본 공격력 + 장비 공격력
데미지 계산식은 몬스터를 공격할 때마다 호출해야 하므로 함수로 만드는 것이 좋을 것이다.
이때, 우리가 입력받아야 할 값은 레벨, 기본 공격력, 장비 공격력이고, 출력해야 할 값은 데미지이다.
이에 따라 총 데미지를 출력하는 함수를 만들어 보자.
static int TotalDamage(int basicAttack, int equipAttack, int level)
{
return level * basicAttack + equipAttack;
}
이 함수를 호출할 때, 기본 공격력 : 10, 장비 공격력 20, 레벨 3이라고 하자.
static void Main(string[] args)
{
int totalDamage = TotalDamage(10, 20, 3);
Console.WriteLine("총 데미지는 {0} 입니다.", totalDamage);
}
결과 출력은 다음과 같이 나온다.
지금은 이렇게 간단하게 데미지를 구할 수가 있겠지만, 게임에서 데미지에 영향을 주는 요소가 계속해서 추가되다 보면 이렇게 단순한 계산식으로 정리하기는 어려울 것이다. 당장 내가 하고 있는 게임 중 메이플스토리만 해도 데미지 계산식이 아주 복잡하다. 입력값을 오류 없이 받은 다음 함수를 통해 출력하는 과정에서 많은 고민이 필요하단 생각이 들었다.
* 또 다른 예시 : 붕어빵 만들기 업그레이드
붕어빵 만들기에서 시간 요소를 추가했다.
이렇게 매개변수가 한 가지 이상의 자료형을 받을 수 있다는 점을 기억해 두자.
static string makeFishCake(string taste, float time)
{
Console.WriteLine("반죽을 틀에 붓습니다");
Console.WriteLine("{0} 재료를 넣습니다", taste);
Console.WriteLine("{0}초 동안 굽습니다.", time);
if (time < 20) // 20초 이내로 구울 시 붕어빵이 타지 않는다.
{
Console.WriteLine("{0} 붕어빵 완성!", taste);
return $"{taste} 붕어빵";
}
else
{
Console.WriteLine("탄 붕어빵 완성!");
return $"탄 붕어빵";
}
}
static void Main(string[] args)
{
string bread = makeFishCake("팥", 11f);
Console.WriteLine("만들어진 붕어빵은 {0}", bread);
}
결과 출력은 다음과 같이 나온다.
반환값이 있는 함수에서 return 아래에 있는 코드는 실행되지 않는다.
결과를 return 하는 순간 함수의 실행은 끝난다고 생각하면 좋다.
이런 점을 활용할 수 있는 방법도 있다.
4)에서 작성한 붕어빵 만들기 업그레이드 버전에서,
사용자가 공백을 기입했을 때 붕어빵을 만들 수 없다는 기능을 추가해 보았다.
static string makeFishCake(string taste, float time)
{
if (taste == "") // 결과를 입력하지 않았을 시 바로 종료되도록 한다.
{
Console.WriteLine("속 없는 붕어빵은 만들 수 없습니다");
return "";
}
Console.WriteLine("반죽을 틀에 붓습니다");
Console.WriteLine("{0} 재료를 넣습니다", taste);
Console.WriteLine("{0}초 동안 굽습니다.", time);
if (time < 20)
{
Console.WriteLine("{0} 붕어빵 완성!", taste);
return $"{taste} 붕어빵";
}
else
{
Console.WriteLine("탄 붕어빵 완성!");
return $"탄 붕어빵";
}
}
이때 속 없는 붕어빵을 만들 수 없다고 출력할 때 아래의 굽는 단계가 실행되지 않도록 return을 하도록 했다.
결과 출력은 다음과 같이 나온다.