함수
함수는 다양한 언어에서 크게 함수, 메서드, 프로시저로 불린다. 하지만 게임 개발자는 주로 함수라고 부른다. 함수는 class 내부에 생성해야 하며, 함수의 형식은 다음과 같다.
// 함수(=Method,메서드)
한정자 반환형식 이름(매개변수목록)
static void HelloWorld()
{
Console.Write.Line("Hello World");
}
static void Main(string[] args)
{
HelloWorld();
}
static은 나중에 class를 설명할 떄 설명할 예정이며, 이름은 함수의 이름을 작성하는 것과 같다. 매개 변수로 넘겨줄 정보가 없으면 아무것도 작성하지 않고, 또 반환 형식으로 함수를 사용 한 이후에 아무런 값을 돌려주지 않는다면 void 키워드를 이용하여 작성한다. 위와 같이 함수를 다 작성하고 Main 클래스에서 HelloWorld() 함수를 호출하면, HelloWorld 함수 내부에 있는 기능이 작동한다.
[디폴트 매개 변수]
class Program
{
static int Add(int a, int b, int c = 0, float d = 1.0f, double e = 3.0)
{
return a + b + c;
}
static void Main(string[] args)
{
Program.Add(1, 2, d:2.0f);
}
}
Add 함수를 호출할 때 매개 변수 c, d, e 는 인수를 넘기지 않더라도 각각의 디폴트 값으로 설정된다.
C++ 에선 인수를 넘길 때 꼭 매개 변수와 대응되게 자리를 맞춰주어야 했지만, C# 에선 위와 같이 d 매개변수에 대해선 인수를 따로 넘겨주고 싶다면 d:2.0f 와 사용하여 값을 넘길 수 있다.
[Call by Value]
Call by Value는 a의 값을 매개변수 AddOne 함수 중괄호 안에서만 수명을 가지는 number에 넘겨준다. 즉, a의 값을 복사하여 number에게 넘겨준 것 뿐 이라, number의 값이 변하여도 a의 값에는 아무런 변화가 없다. 따라서 0이 출력된다. number는 a와 동일한 값을 가진 것일 뿐, 별개의 메모리를 가진다.
class Program
{
static void AddOne(int number)
{
number = number + 1;
}
static void Main(string[] args)
{
int a = 0;
Program.AddOne(a);
Console.WriteLine(a); // 0 출력
}
}
[Call by Reference]
Call by Reference는 a의 값이 아닌 a 메모리 자체를 AddOne 함수 중괄호 안에서만 수명을 가지는 number와 공유한다. 따라서 number의 값이 변하면 a의 값도 변한다. 그래서 a를 출력하면 1이 출력된다. 이 둘은 동일한 메모리를 참조한다.
class Program
{
static void AddOne(ref int number)
{
number = number + 1;
}
static void Main(string[] args)
{
int a = 0;
Program.AddOne(ref a);
Console.WriteLine(a); // 1 출력
}
}
ref, out
class Program
{
static void AddOne(ref int number)
{
number = number + 1;
}
static int AddOne2(int number)
{
return number + 1;
}
static void Main(string[] args)
{
int a = 0;
Program.AddOne(ref a); // 1번 방법
a = Program.AddOne2(a); // 2번 방법
Console.WriteLine(a); // 2 출력
}
}
1번 방법의 방법은 원본 a를 바꿀 수 있는 특성을 가진다. 사본 생성 과정이 없는 것은 효율적이지만 원본을 꼭 변경시킨다는 점도 있다. 따라서 경우에 따라 효율성이 결정될 것이다.
2번 방법은 디자인 상으론 해당 방법이 더 좋다. 왜냐하면 원본 a를 바꾸지 않으면서 b = Program.AddOne2(a) 이렇게 다른 변수에도 값을 리턴시켜 대입할 수 있다. 단 입력 크기가 아주아주 커서 사본 생성에 부담이 따를 때엔 이 방법은 좋지 않을 듯 하다.
[ref]
class Program
{
static void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
static void Main(string[] args)
{
int num1 = 1;
int num2 = 2;
Program.Swap(ref num1, ref num2);
Console.WriteLine(num1); // 2 출력
Console.WriteLine(num2); // 1 출력
}
}
ref는 Call by Reference 를 사용할 때 사용하는 키워드다. 또한 ref int a와 같이 자료형 보다도 앞에 쓰인다. 매개 변수는 & 타입이라도 인수에까지 &을 붙일 필요는 없었던 C++ 문법과는 다르게 C# 에선 넘겨주는 인수까지 꼭 ref를 붙여주어야 한다. 매개 변수가 ref int number로 선언되어 있다면 인수도 마찬가지로 꼭 ref a로 넘겨주어야 한다.
[out]
class Program
{
static void Divide(int a, int b, out int result, out int result2)
{
result = a / b;
result2 = a % b;
}
static void Main(string[] args)
{
int num1 = 10;
int num2 = 3;
Program.Divide(10, 3, out num1, out num2);
Console.WriteLine(num1); // 3 출력
Console.WriteLine(num2); // 1 출력
}
}
out 키워드는 ref 키워드와 똑같다. Call by Reference를 사용할 때 사용한다. 그렇지만 매개 변수에 저장을 강제하는 ref라고 생각하면 된다.
[매개 변수가 ref인 경우]
- 매개 변수에 값을 저장(write)하지 않고 그저 읽기(read)만 하더라도 문법적 오류 전혀 없다.
- → 즉 매개변수를 call by reference 로 인수의 메모리를 참조하기로 해놓고는 아무런 사용도 안해도 문제가 전혀 없다.
- [함수 외부] <-> [함수 내부] 양방향으로 통신하기 위해서 데이터 참조를 주고 받기 위한 개념
- → 읽기 + 쓰기 가능. 사용하지 않아도 됨.
- 함수 내부/외부 사이에 데이터를 빠르게 넘겨주기 위해 사용.
- 반드시 초기화가 되어 있는, 메모리가 비어 있지 않은 변수에만 ref를 사용할 수 있다. 초기화 되지 않은 변수에 ref 붙이면 컴파일 오류가 난다. 함수 내부에서 그냥 읽기만 할 수도 있기 때문에 런타임 에러의 위험이 생길 수 있다.
[매개 변수가 out인 경우]
- 매개 변수에 어떤 값을 저장(write)하는 활동이 전혀 없다면 컴파일 오류를 발생시킨다. 반드시 참조 매개변수를 함수 내부에서 write 시켜야 한다. 단순 읽기만 해서는 안된다.
- call by reference 매개 변수가 참조 중인 메모리의 사용을 강제하기 때문에 ref와 달리 초기화를 전혀 하지 않은 변수를 인수로서 out 매개 변수에 넘기는 것이 가능하다.
- → 함수가 끝나면 넘겨준 인수에도 Call by Reference를 통해 어떤 값이 반드시 저장될 것이라는게 보장되기 때문이다. out이 이를 강제해서 !
- [함수 내부]에서 작업한 최종 결과물을 [함수 외부] 쪽으로 넘겨주는 일방적 통행 (out 이름 답게!)
- → 함수 내부에서 값을 바꿔서 외부에 전달. 참조 중인 외부 메모리에도 영향을 끼침.
- → 반드시 쓰기 과정이 필요
- 함수 내에서의 로직으로 매개 변수 write을 진행한 최종 데이터를 함수 외부로 넘겨주기 위한 용도로 사용.
위와 같이 out이 붙은 매개 변수에 아무런 값도 저장하지 않는다면 이렇게 컴파일 오류가 뜬다. out 매개변수에 저장을 강제하기 때문이다. 만약 ref였으면 아무런 문제가 없다. 해당 오류는 위 코드와 같이 out 매개 변수에 값을 저장하면 오류가 사라진다. 마찬가지로 인수에도 모두 out을 붙여주어야 한다.
'공부 > 인프런 - Rookiss' 카테고리의 다른 글
Part 1-4-1. 객체지향 : 객체 지향, 복사와 참조, 스택과 힙 (0) | 2023.08.02 |
---|---|
Part 1-3-1. TextRPG : 디버깅 기초, 직업, 플레이어/몬스터 생성, 전투 (0) | 2023.08.02 |
Part 1-2-1. 코드의 흐름 제어 : if와 else, switch, 상수와 열거형, while, for, break, continue (0) | 2023.07.31 |
Part 1-1-2. 데이터 : 형변환, 스트링 포맷, 산술 연산, 비교 연산, 논리 연산 (0) | 2023.07.27 |
Part 1-1-1. 데이터 : 데이터 형식, 변수, 정수 형식, 2진수, 10진수, 16진수, 정수 범위 (0) | 2023.07.26 |