프로그래밍 언어 - C#(5) 함수
1. 함수
1.1. 개요: 뜬금없이 자료형?
- 값 타입, 참조 타입에 관한 사항을 자료형을 할 때 언급을 했었음.
- 이것을 언급하는 이유는 함수에 들어가기 전에 이 2개에 대해서 조금 더 알고 들어갈 필요가 있기 때문이다.
- 값 타입의 대표적인 것은 int, float, char, bool 등이 있을 것임.
- 참조 타입에는 string, 배열 정도가 지금 배운 것에서 알 수 있는 것들이다.
- 이제 이것이 어떻게 동작하는지를 알아야 한다는 것.
- 값 타입의 경우, 변수를 선언했을 때 메모리 상에 올라가게 된다.

<그러니까 값 타입은 이렇게 직접 데이터를 들고 있다>
- int a = 15;라고 한다면 a라는 이름의 데이터를 메모리 상에 등록을 한다. 즉, 값 타입은 자신의 데이터를 스스로 직접 가지고 있는 형태이다.
- 참조 타입의 경우는 좀 다른데 int[] arr = new int [5];라고 하나 선언한다고 한다면 실제로 메모리 상에서는 변수가 할당되는 것까지는 동일하나 메모리 안에서는 배열의 데이터가 할당이 되었을 것인데(수정 등이 가능하므로) 이 arr 변수 자체가 아닌 별도의 공간에 5칸짜리 int 타입을 넣을 수 있는 것이 할당되고 첫번째 위치를 가리키는 원본 데이터의 주소값(포인터)을 가진다.
- RAM은 프로그램이 실행될 때, RAM에 올려두고 데이터를 꺼내다가 쓰는 것.
- 즉, 참조 타입은 첫번째 데이터의 주소값을 가지고 있을 뿐이다.

<참조 타입은 사물함 번호 혹은 몇번째 가방인가이다>
- 이 개념은 C#에서 가장 중요한 개념이다.
- 배열의 경우는 0번부터 시작할탠데 선언한 int는 4byte는 index로 접근이 빠르다. 메모리 주소값이 첫번째를 가리키기 때문에 0번 메모리에서 12 byte만 가면 되기 때문에 접근이 용이하다는 것.
- string를 도식화 해보면 string text = "sdf";를 했을 때 text라는 변수는 메모리 주소값을 가지고 어딘가에 char 배열을 가지게 된다. 배열이 또 참조 타입이므로 또 다른 배열을 가지게 된다.
- text라는 변수에서는 string 내부 데이터의 참조를 가지고 있고 또 그 배열의 참조가 들어가서 2중 배열 구조처럼 된다.
- 이것을 알면 함수에서의 키워드를 알기 쉽게 된다.
1.2. 함수
- 변수 : 변할 수 있고 데이터에 닉네임을 지어주는 용도로 사용.
- 함수 : 기능에 별명을 지어주고 필요할 때 쓰는 것.

<이름을 지으면 그대로 그 이름으로 적용된다>
- 특정 코드 단락을 묶어두고 별명을 지어주는 것이 함수이다.
- 함수에도 동일하게 쓰는 키워드가 있는데 변수에서 선언과 대입이 있다면 함수는 선언은 동일하나 함수를 갖다가 쓸 때는 호출이라고 부른다.
- 실제로 사용해보도록 하자.
// 형태 : [반환형] [닉네임]() {}
using System;
class Program
{
static void Main(string[] args)
{
// 변수 : 변할 수 있고 데이터에 닉네임을 지어주는 용도로 사용.
// 함수 : 기능에 닉네임을 지어주는 용도.
// 함수 선언
// 랜덤한 숫자를 뽑고 싶을 때, 어제의 경우
// Random rand = new Random();
// int a = rand.Next(-100, 100);
Console.WriteLine(GetRandomNumber());
Console.WriteLine(GetRandomNumber());
Console.WriteLine(GetRandomNumber());
Console.WriteLine(GetRandomNumber());
Console.WriteLine(GetRandomNumber());
}
// 이제는 함수 형태로 해보도록 할 것.
// 위는 int 타입이 나오므로 동일하게 int가 되어야 한다.
static int GetRandomNumber()
{
Random rand = new Random();
int a = rand.Next(-100, 100);
return a;
}
// GetRandomNumber()는 함수 내부에서 이런 코드를 수행한다.
}
- 함수는 코드 한 덩어리에다가(기능 단위에다가) 이름을 붙여둔 다음에 필요할 때 이름으로 호출해서 쓰는 것이 함수이다.
- 선언할 때, 어떤 데이터가 반환되는지를 알려주고 이름에다가 괄호를 붙이고 중괄호에 내부 기능을 정의해준다의 순서이다.
- 반환형이 있을 경우, 함수 내부에서 기능 수행 후 외부에 뽑아주는 것을 반환한다고 표현한다.
- 프로그래머스에서 시작할 때, 반환형이 있고 이름이 붙어있고 괄호가 있는데 이게 함수라고 생각하면 된다. 시스템 내부적으로 데이터를 외부에서 넣고 함수 내에서 데이터를 반환해주는 역할을 return이 한다.
- 함수로 기능을 묶을 수 있게 되어 코드 중복을 줄일 수 있고 어떤 기능을 하는 것인지 직관적으로 알 수 있게끔 해준다.

<함수는 분리수거처럼 필요한 것을 묶어 관리한다>
1.3. void
- 실동작은 하지만 반환값이 없는 것을 할 때 void. main도 void 함수이다.
using System;
class Program
{
static void Main(string[] args)
{
PrintTitle();
}
static int GetRandomNumber()
{
Random rand = new Random();
int a = rand.Next(-100, 100);
//return a;
}//에러 발생
static void PrintTitle()
{
Console.WriteLine("====================================================");
Console.WriteLine(" 땳똷뚫 ");
Console.WriteLine("====================================================");
}
}
- 반환형이 있는데 return이 없으면 error 발생.
- void는 return이 없고 이 기능만을 수행하는 역할이다.
2. 매개변수
2.1. 개요
- 함수의 괄호 안에 변수가 선언된 상태로 들어가 있는 것들을 매개변수라고 부른다.

<강화라는 함수에서는 강화 재료라는 매개변수가 있다고 생각하면 좋다>
2.2. 사용
- 바로 코드부터
using System;
class Program
{
static void Main(string[] args)
{
int a = Sum(7, 14);
Console.WriteLine(a);
}
// 함수 내에서 쓸 수 있는 변수로 동작함.
static int Sum(int a, int b)
{
int sum = a + b;
return sum;
}
}
- 외부에서 데이터를 받아서 함수에서 내부적으로 돌아간 다음, 반환해준다를 보여주는 형태.
- 만약 입력받아야 할 값을 입력받지 못하면 에러 발생.
- 형태는 지금까지 4가지 정도임.
3. 키워드
3.1. ref
- 서로 바꾸는 swap 함수를 살펴보자.
using System;
class Program
{
static void Main(string[] args)
{
int a = 5;
int b = 8;
ChangeNumber(a, b);
Console.WriteLine($"{a}, {b}");
}
static void ChangeNumber(int a, int b)
{
int tempNumber = 0;
tempNumber = a;
a = b;
b = tempNumber;
Console.WriteLine($"Swap : {a}, {b}");
}
}
- 이러면 실사용 시 함수 내부에서만 동작하고 외부로 내보내지지 않는다.
- 그런데 ref를 여기다가 붙이면?
using System;
class Program
{
static void Main(string[] args)
{
int a = 5;
int b = 8;
ChangeNumber(ref a, ref b);
Console.WriteLine($"{a}, {b}");
}
static void ChangeNumber(ref int a, ref int b)
{
int tempNumber = 0;
tempNumber = a;
a = b;
b = tempNumber;
Console.WriteLine($"Swap : {a}, {b}");
}
}
왜 이럴까? 기존의 것을 한 번 보도록 하자.
using System;
class Program
{
static void Main(string[] args)
{
int a = 5;
int b = 8;
ChangeNumber(a, b);
Console.WriteLine($"{a}, {b}");
}
static void ChangeNumber(int a, int b)
{
int tempNumber = 0;
tempNumber = a;
a = b;
b = tempNumber;
Console.WriteLine($"Swap : {a}, {b}");
}
}
- a와 b에 메모리가 할당되어 데이터를 가지게 된다(값 타입).
- 그리고나서 changeNumber 함수를 만나고 어떻게 동작할까?
- changeNumber에 Main에서 a, b를 넘겨준다.
- 여기서 a, b가 통째로 들어가는 것이 아니라 함수 내부에서 복사본이 발생한다.
- 함수 내부에서만 결과가 바뀐다.
- 그러나 값 타입에서는 데이터가 바뀐 것은 복사본이다.

<암만 복사본을 변경해봐야 적용이 안된다>
- 결과적으로 복사본만 수행되고 말아버리기 때문에 출력이 제대로 이뤄지지 않았던 것.
- 그렇다면 ref에서는 어떻게 되는지?
- 복사본이 만들어지기까지 동일하다.
- 하지만 값을 넘기는 때에 a값이 있는 주소와 b값이 있는 주소값을 넘겨서 복사되는 것이다.
- 그래서 주소로 찾아가서 데이터를 실질적으로 바꿔준다.

<복사된 주소값을 알고 있으니 데이터 변동이 실질적으로 이뤄진다>
- 결과적으로 ref를 붙이지 않는 이상 값이 죽어도 안바뀐다.
- 위 ref가 안붙은 것을 CbV, Call by Value라고 부른다.ref가 붙는 것을 CbR, Call by Reference이다.
- 두가지는 현상에 대한 호칭일 뿐이다.
3.2. out
using System;
class Program
{
static void Main(string[] args)
{
string text = "1124";
int num;
int.TryParse(text, out num); // ref 대신 쓸 수 있는 키워드. 원본을 넘겨주는 형태.
}
}
- 변수에 어떤 값이 있는지 조회는 불가하다.
- 그래서 데이터를 뭘 받는지는 알 수 없으나 함수 내부에서 결과값을 외부로 줄 때 사용한다.

<일단 결과값은 손에 들어오는 형식이다>
3.3. in
- foreach에서 봤듯, 원본을 조회할 수 있지만 데이터를 바꿀 수 없다.

<아무리 사용하고 싶어도 할 수 없다>
3.4. 코드로 보는 예시
using System;
class Program
{
static void Main(string[] args)
{
int num = 5;
}
static void foo()
{
}
}
- 준비가 되었다면 입력받은 값을 10으로 바꿔보도록 하자.
using System;
class Program
{
static void Main(string[] args)
{
int num = 5;
foo(num);
Console.WriteLine(num);
}
static void foo(int a)
{
a = 10;
}
using System;
class Program
{
static void Main(string[] args)
{
int num = 5;
foo(ref num);
Console.WriteLine(num);
}
static void foo(ref int a)
{
a = 10;
}
}
- 정상 출력
using System;
class Program
{
static void Main(string[] args)
{
int num = 5;
foo(out num);
Console.WriteLine(num);
}
static void foo(out int a)
{
a = 10;
}
}
- 정상 출력
using System;
class Program
{
static void Main(string[] args)
{
int num = 5;
foo(out num);
Console.WriteLine(num);
}
static void foo(out int a)
{
Console.WriteLine(a);
a = 10;
}
- 오류 발생. 값을 수정은 가능하나 바로 조회는 불가. 수정 후 조회는 가능.
using System;
class Program
{
static void Main(string[] args)
{
int num = 5;
foo(out num);
Console.WriteLine(num);
}
static void foo(out int a)
{
a = 10;
Console.WriteLine(a);
}
- 정상 출력. 이번엔 in으로 바꿔본다.
using System;
class Program
{
static void Main(string[] args)
{
int num = 5;
foo(in num);
Console.WriteLine(num);
}
static void foo(in int a)
{
Console.WriteLine(a);
a = 10;
}
}
- 오류 발생. 보는 것은 가능할지언정 수정은 불가하다.
- 이 3개를 매개변수 한정자라고 부른다.
3.5. overloading
- swap 함수를 중복해서 쓰고 싶지만 가령 매개변수가 int로 되어 있는 것을 float를 입력할 수 없다.
using System;
class Program
{
static void Main(string[] args)
{
float a = 3.55f;
float b = 2.6f;
ChangeNumber(a, b); // 쓸데없이 많이 붙게 된다. 함수가 여러개 만들어지게 되는 셈.
}
static void ChangeNumber(int a, int b)
{
int tempNumber = 0;
tempNumber = a;
a = b;
b = tempNumber;
Console.WriteLine($"Swap : {a}, {b}");
}
static void ChangeNumber(float a, float b)
{
float tempNumber = 0;
tempNumber = a;
a = b;
b = tempNumber;
Console.WriteLine($"Swap : {a}, {b}");
}
}
- 이름을 같게 하고 매개변수가 다를 때, 오버로딩이 발생하여 구분지을 수 있게 된다.
- 또한 매개변수의 개수가 기준이 될 수도 있다.
using System;
class Program
{
static void Main(string[] args)
{
int a = 1;
int b = 2;
int c = 3;
ChangeNumber(a, b);
ChangeNumber(a, b, c); // 쓸데없이 많이 붙게 된다. 함수가 여러개 만들어지게 되는 셈.
}
static void ChangeNumber(int a, int b)
{
int tempNumber = 0;
tempNumber = a;
a = b;
b = tempNumber;
Console.WriteLine($"Swap : {a}, {b}");
}
static void ChangeNumber(float a, float b)
{
float tempNumber = 0;
tempNumber = a;
a = b;
b = tempNumber;
Console.WriteLine($"Swap : {a}, {b}");
}
static void ChangeNumber(int a, int b, int c)
{
int tempNumber = 0;
tempNumber = a;
a = b;
b = tempNumber;
Console.WriteLine($"Swap : {a}, {b}");
}
}
-
이렇게.
-
그러나 반환 형식만 다른 것은 오버로딩이 되지 않는다.(즉, return이 다른 것)
static int GetRandomNumber()
{
Random rand = new Random();
int a = rand.Next(-100, 100);
return a;
}
static int GetRandomNumber()
{
return 3.14f;
}
- 오버로딩과 오버라이딩의 차이에 대해서 물을 수 있음.
- 오버로딩은 함수(메서드) 이름의 재사용, 오버라이딩은 부모에게 받은 함수를 자식에 맞춰 재정의를 하는 것이다.
- 일단 오버로딩은 이렇게 알려준대로 생각하면 된다.
3.6. 재귀 호출
- 보통 알고리즘에서 쓰임.
- 없어도 알고리즘을 짜는 것에 이상은 없지만 사용하는 이유는 반복해야 하는 알고리즘에서 좀 더 코드를 간결하게 만들 수 있게 된다.
- 팩토리얼 문제가 그 예시.
using System;
class Program
{
static void Main(string[] args)
{
Foo();
}
static void Foo()
{
int a = 5;
Foo();
}
}
- stack overflow 발생.
- Foo 함수를 호출하게 되면 내부 기능들이 실행될탠데 일단 a라는 변수가 할당될 것이다.
- a 변수가 할당되고 5라는 것이 들어갔다. 그리고 다시 Foo가 실행되고 a라는 변수가 또 생기고 5가 담긴다.
- 이게 계속해서 돌아간다는 것. 메모리 하나씩 하나씩 잡아먹고 메모리의 빈 공간이 없게 되어 결과적으로 터지는 코드가 되는 것.
- 재귀 함수의 형태는 위와 같은 구조와 비슷하다.
- 그래서 재귀함수를 다룰 때 가장 주의해야 할 것은 기저조건을 설정해줘야 한다는 것이다.

<탈출 조건을 만들어야 한다는 것>
- 호출 스택이 어느 기준에서 멈춰줘야 한다는 조건을 설정해줘야 한다는 의미.
using System;
class Program
{
static void Main(string[] args)
{
Foo(3);
}
static void Foo(int count)
{
Console.WriteLine("Foo 함수 호출");
// 기저 조건.
if (count == 0)
{
Console.WriteLine("재귀 끝");
return; // 반복문에서 break와 같은 역할을 수행.
}
Foo(count - 1);
}
}
- count에 3이 들어가고 기저조건에 안걸린다. 기저조건에 걸리지 않으므로 Foo 호출까지 완료
- 한 번 더 수행이 되었을 때 count가 2가 들어오고 기저조건에 안걸려서 Foo가 또 호출.
- 그러다가 count가 0이 들어오고 기저조건 쪽에서 if문에 걸리니까 재귀 끝이 호출되고 나서 return을 만나 끝난다.
- 이 상태에선 Foo를 호출했었으니 함수를 호출되었던 곳으로 돌아가게 된다.
- 다시 거슬러 올라가서 첫번째 count 3에 돌아가게 된다.
using System;
class Program
{
static void Main(string[] args)
{
Foo(3);
}
static void Foo(int count)
{
Console.WriteLine($"Foo 함수 호출: {count}");
// 기저 조건.
if (count == 0)
{
Console.WriteLine("재귀 끝");
return; // 반복문에서 break와 같은 역할을 수행.
}
Foo(count - 1);
Console.WriteLine($"Foo 함수 종료: {count}");
}
}
출력하면 역순으로 나가는 것을 확인할 수 있음.
4.응용
4.1. 함수화
using System;
class Program
{
static void Main(string[] args)
{
int mapSize = 10;
char[,] map = new char[mapSize, mapSize];
for (int y = 0; y < mapSize; y++)
{
for (int x = 0; x < mapSize; x++)
{
map[y, x] = '.';
}
}
int playerPositionX = 0;
int playerPositionY = 0;
map[playerPositionY, playerPositionX] = 'p';
bool isPlaying = true;
while (isPlaying)
{
// Render
Console.Clear();
for (int y = 0; y < mapSize; y++)
{
for (int x = 0; x < mapSize; x++)
{
Console.Write(map[y, x]);
Console.Write(' ');
}
Console.WriteLine();
}
// Input
char userInput = char.ToUpper(Console.ReadKey(true).KeyChar);
int nextX = playerPositionX;
int nextY = playerPositionY;
switch (userInput)
{
case 'W':
nextY--;
break;
case 'S':
nextY++;
break;
case 'A':
nextX--;
break;
case 'D':
nextX++;
break;
case 'C':
isPlaying = false;
break;
}
// 배열 범위 밖 처리
bool canMove = nextY >= 0 && nextX >= 0 && nextY < mapSize && nextX < mapSize;
if (!canMove)
{
continue;
}
map[playerPositionY, playerPositionX] = '.';
playerPositionX = nextX;
playerPositionY = nextY;
map[playerPositionY, playerPositionX] = 'p';
}
}
}
- 여기서 시작.
- 먼저 맵 초기화부터 함수화를 시작해보도록 하자.
using System;
class Program
{
static void Main(string[] args)
{
int mapSize = 10;
// 맵 초기화
char[,] map = GetInitalizeMap(mapSize);
// 플레이어 위치
int playerPositionX = 0;
int playerPositionY = 0;
map[playerPositionY, playerPositionX] = 'p';
bool isPlaying = true;
while (isPlaying)
{
// 맵 출력
Console.Clear();
for (int y = 0; y < mapSize; y++)
{
for (int x = 0; x < mapSize; x++)
{
Console.Write(map[y, x]);
Console.Write(' ');
}
Console.WriteLine();
}
// 유저 입력 받아서 움직일 방향 설정
char userInput = char.ToUpper(Console.ReadKey(true).KeyChar);
int nextX = playerPositionX;
int nextY = playerPositionY;
switch (userInput)
{
case 'W':
nextY--;
break;
case 'S':
nextY++;
break;
case 'A':
nextX--;
break;
case 'D':
nextX++;
break;
case 'C':
isPlaying = false;
break;
}
// 배열 범위 밖 처리
bool canMove = nextY >= 0 && nextX >= 0 && nextY < mapSize && nextX < mapSize;
if (!canMove)
{
continue;
}
// 실제 맵에 적용.
map[playerPositionY, playerPositionX] = '.';
playerPositionX = nextX;
playerPositionY = nextY;
map[playerPositionY, playerPositionX] = 'p';
}
}
static char[,] GetInitalizeMap(int mapSize) // 2. 매개변수 적용 //3. 실제 맵에 적용에서 map에 대한 오류 발생.
{
char[,] map = new char[mapSize, mapSize]; // 1. mapsize를 받을 필요가 있을 것.
for (int y = 0; y < mapSize; y++)
{
for (int x = 0; x < mapSize; x++)
{
map[y, x] = '.';
}
}
return map; // 4. 반환값 설정
}
}
- 실질적으로 함수안에 기능들을 밀어넣고 main에다가 char[,] map = GetInitalizeMap(mapSize);만 해서 집어넣는 결과가 된다.
- 다음 항목인 플레이어 초기 위치부터 함수화 해보도록 하자.
using System;
class Program
{
static bool isPlaying = true; // 프로그램 밖에서 쓰려면 static이라는 개념이 필요하다 정도로만 인지.
static void Main(string[] args)
{
int mapSize = 10;
// 맵 초기화
char[,] map = GetInitalizeMap(mapSize);
// 플레이어 초기위치 설정
int playerX = 0;
int playerY = 0;
SetPlayerPosition(map, playerX, playerY);
while (isPlaying)
{
// 맵 출력
PrintMap(map, mapSize);
// 유저 입력 받아서 움직일 방향 설정
int nextX;
int nextY;
SetNextPosFromUserInput(playerX, playerY, out nextX, out nextY);
// 배열 범위 밖 처리
if (!CanMove(nextX, nextY, mapSize))
{
continue;
}
// 실제 맵에 적용.
SetPlayerPosition(map, nextX, nextY, ref playerX, ref playerY);
}
}
static char[,] GetInitalizeMap(int mapSize) // 2. 매개변수 적용 //3. 실제 맵에 적용에서 map에 대한 오류 발생.
{
char[,] map = new char[mapSize, mapSize]; // 1. mapsize를 받을 필요가 있을 것.
for (int y = 0; y < mapSize; y++)
{
for (int x = 0; x < mapSize; x++)
{
map[y, x] = '.';
}
}
return map; // 4. 반환값 설정
}
// 1. 몇, 몇이 필요함.
static void SetPlayerPosition(char[,] map, int x, int y) // 2. 매개변수 설정 // 4. 이차원 배열 매개변수 추가.
{
map[y, x] = 'p'; // 3. 뭔지 모르므로 받아와야 한다.
}
static void PrintMap(char[,] map, int mapSize) // 2. 이미 main에 있는 size 받아올 것.
{
Console.Clear();
for (int y = 0; y < mapSize; y++) // 1. 뭔지 모르겠다. 그런데 2차원 배열을 받음. 차원별 배열 크기를 알 수 있음(GetLength).
{
for (int x = 0; x < mapSize; x++)
{
Console.Write(map[y, x]);
Console.Write(' ');
}
Console.WriteLine();
}
}
static void SetNextPosFromUserInput(int playerX, int playerY, out int nextX, out int nextY) // 1. 기존 플레이어 위치를 알아야 한다.
{
char userInput = char.ToUpper(Console.ReadKey(true).KeyChar);
nextX = playerX; // 2. 변수명이 겹쳐서 받아다가 쓰는 것으로 바꾸도록 한다.
nextY = playerY;
switch (userInput)
{
case 'W':
nextY--;
break;
case 'S':
nextY++;
break;
case 'A':
nextX--;
break;
case 'D':
nextX++;
break;
case 'C':
isPlaying = false; // 3. 이것의 상태를 바꿔줘야 한다. 이것은 전역적으로 쓰인다. 매개변수도 가능은 하나 메인 밖에 빼자.
break;
}
}
static bool CanMove(int nextX, int nextY, int mapSize) // 2. 매개변수 설정.
{
bool canMove = nextY >= 0 && nextX >= 0 && nextY < mapSize && nextX < mapSize; // 1. 필요 변수 발생.
return canMove; //3. T/F 결과값.
}
static void SetPlayerPosition(char[,] map, int nextX, int nextY, ref int playerX, ref int playerY)
{
map[playerY, playerX] = '.';
map[nextY, nextX] = 'P';
playerX = nextX;
playerY = nextY;
}
}
4.2. optional parameter
- 아까 만든 오버로딩 함수를 변환해보도록 하자.
// optional parameter. 사용하는 사람이 따로 설정하지 않으면 설정한 기본값으로 들어가는 것.
static void SetPlayerPosition(char[,] map, ref int playerX, ref int playerY, int nextX = -1, int nextY = -1)
{
if (nextX == -1 || nextY == -1)
{
map[playerY, playerX] = 'P';
}
else
{
map[playerY, playerX] = '.';
map[nextY, nextX] = 'P';
playerX = nextX;
playerY = nextY;
}
}
using System;
class Program
{
static bool isPlaying = true; // 프로그램 밖에서 쓰려면 static이라는 개념이 필요하다 정도로만 인지.
static void Main(string[] args)
{
int mapSize = 10;
// 맵 초기화
char[,] map = GetInitalizeMap(mapSize);
// 플레이어 초기위치 설정. 기존 것을 지워준다.
int playerX = 0;
int playerY = 0;
while (isPlaying)
{
// 맵 출력
PrintMap(map, mapSize);
// 유저 입력 받아서 움직일 방향 설정
int nextX;
int nextY;
SetNextPosFromUserInput(playerX, playerY, out nextX, out nextY);
// 배열 범위 밖 처리
if (!CanMove(nextX, nextY, mapSize))
{
continue;
}
SetPlayerPosition(map, ref playerX, ref playerY, nextX, nextY); // 이러면 뒤의 것은 설정한 else문에서 작동
}
}
static char[,] GetInitalizeMap(int mapSize) // 2. 매개변수 적용 //3. 실제 맵에 적용에서 map에 대한 오류 발생.
{
char[,] map = new char[mapSize, mapSize]; // 1. mapsize를 받을 필요가 있을 것.
for (int y = 0; y < mapSize; y++)
{
for (int x = 0; x < mapSize; x++)
{
map[y, x] = '.';
}
}
return map; // 4. 반환값 설정
}
static void PrintMap(char[,] map, int mapSize) // 2. 이미 main에 있는 size 받아올 것.
{
Console.Clear();
for (int y = 0; y < mapSize; y++) // 1. 뭔지 모르겠다. 그런데 2차원 배열을 받음. 차원별 배열 크기를 알 수 있음(GetLength).
{
for (int x = 0; x < mapSize; x++)
{
Console.Write(map[y, x]);
Console.Write(' ');
}
Console.WriteLine();
}
}
static void SetNextPosFromUserInput(int playerX, int playerY, out int nextX, out int nextY) // 1. 기존 플레이어 위치를 알아야 한다.
{
char userInput = char.ToUpper(Console.ReadKey(true).KeyChar);
nextX = playerX; // 2. 변수명이 겹쳐서 받아다가 쓰는 것으로 바꾸도록 한다.
nextY = playerY;
switch (userInput)
{
case 'W':
nextY--;
break;
case 'S':
nextY++;
break;
case 'A':
nextX--;
break;
case 'D':
nextX++;
break;
case 'C':
isPlaying = false; // 3. 이것의 상태를 바꿔줘야 한다. 이것은 전역적으로 쓰인다. 매개변수도 가능은 하나 메인 밖에 빼자.
break;
}
}
static bool CanMove(int nextX, int nextY, int mapSize) // 2. 매개변수 설정.
{
bool canMove = nextY >= 0 && nextX >= 0 && nextY < mapSize && nextX < mapSize; // 1. 필요 변수 발생.
return canMove; //3. T/F 결과값.
}
// optional parameter. 사용하는 사람이 따로 설정하지 않으면 설정한 기본값으로 들어가는 것.
static void SetPlayerPosition(char[,] map, ref int playerX, ref int playerY, int nextX = -1, int nextY = -1)
{
if (nextX == -1 || nextY == -1)
{
map[playerY, playerX] = 'P';
}
else
{
map[playerY, playerX] = '.';
map[nextY, nextX] = 'P';
playerX = nextX;
playerY = nextY;
}
}
}




