## 목차
1) 메소드란?
2) return에 대하여
3) 매개변수에 대하여
4) 참조에 의한 매개변수 전달
5) 메소드의 결과를 참조로 반환하기
6) 출력 전용 매개변수
#1 메소드란?
메소드는 일련의코드를 하나의 이름 아래 묶은 것입니다. 이렇게 묶은 코드는 메소드의 이름을 불러주는 것만으로 실행할 수 있습니다. 이것을 일컬어 '메소드를 호출한다' 고 합니다.
class 클래스_이름
{
// 한정자 : 메소드의 속성을 수삭하는 한정자를 둘 수 있습니다.
// 매개변수_목록 : 메소드에 입력하는 매개변수의 목록입니다.
한정자 반환_형식 메소드_이름(매개변수_목록)
{
// 실행하려는 코드 1
// 실행하려는 코드 2
// ..
// 실행하려는 코드 n
// 메소드 결과의 데이터 형식은 메소드의 반환 형식과 일치해야 합니다.
return 메소드_결과;
}
}
메소드가 함수, 프로시저, 서브루틴 등과 가른 점이라면 바로 이 클래스 안에 존재한다는 것입니다.
C#은 객체지향 프로그래밍 언어이고, 코드 내 모든 것을 객체로 표현합니다. 각 객체는 자신만의 속성(데이터)과 기능(메소드)을 갖고 있는데, 클래스가 바로 이 객체를 위한 청사진을 제공합니다. 그래서 메소드가 클래스 안에서 선언되는 것입니다.
또한, 메소드는 객체의 일을 처리하는 방법 또는 방식으로 이해하면 도움이 될 것 같습니다.
클래스 안에 선언되는 메소드는 매개변수와 반환 형식을 가지는데, 매개변수는 제품을 만들기 위해 기계(메소드)에 집어넣는 재료라고 할 수 있고, 이 기계(메소드)는 입력된 재료(매개변수)를 바탕으로 제품을 만들어내며, 메소드의 반환 형식은 이 제품의 규격이라고 할 수 있습니다.(*반환? 메소드의 호출자로부터 입력을 받았으니 결과를 돌려준다는 의미)
// ...
int a = 3;
int b = 4;
int x = Calculator.Plus(3, 4);
// ...
public static int Plus(int a, int b)
{
Console.WriteLine("Input : {0}, {1}", a, b);
int result = a + b;
return result;
}
- Calculator.Plus() 메소드를 호출했습니다.
- 이 때, Plus() 메소드로 이동한 후 이 메소드가 담고 있는 코드를 차례대로 수행합니다.
- 메소드 블록의 끝에 도달하거나 Plus() 메소드에서처럼 return 문을 만나면 메소드가 종결됩니다.
- 원래의 프로그램 흐름으로 돌아와 계속 실행합니다.
- Plus() 메소드는 int x = Calculator.Plus(3, 4); 식의 코드에 실행 결과를 반환합니다.
[실습]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace ThisIsCSharpExam.Ch._06.MethodExam
{
class CalculatorExam
{
public void Main()
{
int result = CalculatorExam.Plus(3, 4);
Console.WriteLine(result);
result = CalculatorExam.Minus(5, 2);
Console.WriteLine(result);
}
public static int Plus(int a, int b)
{
return a + b;
}
public static int Minus(int a, int b)
{
return a - b;
}
}
}
void 반환 형식
모든 메소드들이 결과를 반환하는 것은 아닙니다. 어떤 메소드들은 자기 할 일만 하고 종료합니다. Console 클래스의 Write()나 WriteLine() 메소드가 그런 예입니다. 이런 메소드를 선언할 때는 반환 형식에 void를 넣어주면 됩니다.
static 한정자
사전적으로 정적(움직이지 않는다)이라는 뜻을 가지고 있습니다. C#에서 static은 메소드나 필드가클래스의 인스턴스가 아닌 클래스 자체에 소속되도록 지정하는 한정자입니다. 조금 전 예제의 Plus() 메소드와 Minus() 메소드처럼 static 한정자로 수식하면 CalculatorExam의 인스턴스를 만들지 않고도 해당 메소드를 호출할 수 있습니다.
#2 return에 대하여
return 문은 점프문의 한 종류입니다. 프로그램의 흐름을 갑자기 호출자에게로 돌려놓죠. return 문은 언제든지 메소드 중간에 호출하여 메소드를 종결시키고 프로그램의 흐름을 호출자에게 돌려줄 수있습니다.
메소드가 반환할 것이 아무것도 없는 경우, 즉 반환 형식이 void인 경우에도 return 문을 사용할 수 있습니다.
재귀 호출(Recursive Call)
메소드가 자기 자신을 스스로 호출하는 것을 일컬어 재귀 호출 이라고 합니다. 아래의 Fibonacci() 함수가 그 예입니다. 재귀 호출은 코드를 단순하게 구성할 수 있다는 장점이 있는 한편, 성능에는 나쁜 영향을 주기 때문에 주의해서 사용해야 합니다.
[실습]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ThisIsCSharpExam.Ch._06.MethodExam
{
class ReturnExam
{
public void Main()
{
Console.WriteLine($"10번째 파보나치 수 : {Fibonacci(10)}");
PrintProfile("", "123-4567");
PrintProfile("씨샵이", "456-1230");
}
static int Fibonacci(int n)
{
if (n < 2)
return n;
else
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
static void PrintProfile(string name, string phone)
{
if (name == "")
{
Console.WriteLine("이름을 입력해주세요.");
return;
}
Console.WriteLine($"Name:{name}, Phone:{phone}");
}
}
}
#3 매개변수에 대하여
class Calculator
{
public static int Plus(int a, int b)
{
Console.WriteLine("Input : {0}, {1}", a, b);
int result = a + b;
return result;
}
// ...
}
class MainApp
{
public static void Main()
{
int x = 3;
int y = 4;
int result = Calculator.Plus(x, y);
//...
}
}
예제를 먼저 보면서 이해해보겠습니다.
Main() 메소드 안에서 Calculator.Plus() 메소드에 매개변수를 넘기는 코드를주목하기 바랍니다. x와 y를 인수로 넘기고 있습니다. x는 Plus() 메소드의 매개변수 a로서, y는 b로서 넘겨집니다. 여기서 MainApp.Main() 메소드 안에서 선언되어 Calculator.Plus() 메소드에 인수로 넘겨진 x와 y는 실제로 Calculator.Plus() 메소드 안으로 들어가는 것이 아닙니다.
메소드 외부로부터 메소드 내부로 데이터를 전달받는 매개체 역할을 할 뿐이지, 매개변수도 근본적으로는 '변수' 입니다. 한 변수를 또 다른 변수에 할당하면 변수가 담고 있는 데이터만 복사될 뿐입니다. 그 데이터가 값이든 참조든 간에 말입니다. Calculator.Plus() 메소드가 호출될 경우 x가 담고 있는 데이터 3은 매개변수 a로, y는 4의 데이터로 b에 복사가 이루어집니다.
a는 x가 가진 것과 똑같은 데이터를 갖고 있지만, a와 x는 완전히 별개의 메모리 공간을 사용합니다. b와 y도 마찬가지입니다. 따라서 a를 수정한다고 해도 x는 아무런 영향을 받지 않습니다.
이처럼 메소드를 호출할 때 데이터를 복사해서 매개변수에 넘기는 것을 '값에 의한 전달'이라고 부릅니다.
[실습]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ThisIsCSharpExam.Ch._06.MethodExam
{
class SwapByValueExam
{
public void Main()
{
int x = 3;
int y = 4;
Console.WriteLine($"x:{x}, y:{y}");
Swap(x, y);
Console.WriteLine($"x:{x}, y:{y}");
}
public static void Swap(int a, int b)
{
int temp = b;
b = a;
a = temp;
}
}
}
#4 참조에 의한 매개변수 전달
Swap() 메소드가 이름값을 하도록, 즉 두 매개변수의 값을 제대로 교환할 수 있도록 하는 방법도 있습니다.
바로 매개변수를 '참조에 의한 전달'로 넘기면 Swap() 메소드가 제대로 일할 수 있습니다. 값에 의한 전달이 매개변수가 변수나 상수로부터 값을 복사하는 것과 달리 참조에 의한 전달은 매개변수가 메소드에 넘겨진 원본 변수를 직접 참조합니다.
참조에 의한 매개변수 전달은 ref 키워드의 도움만 받으면 아주 쉽습니다.
[실습]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ThisIsCSharpExam.Ch._06.MethodExam
{
class SwapByRefExam
{
public void Main()
{
int x = 3;
int y = 4;
Console.WriteLine($"x:{x}, y:{y}");
Swap(ref x, ref y);
Console.WriteLine($"x:{x}, y:{y}");
}
public static void Swap(ref int a, ref int b)
{
int temp = b;
b = a;
a = temp;
}
}
}
#5 메소드의 결과를참조로 반환하기
메소드의 결과를 참조로 반환하는 참조 반환값에 대해서 학습해보겠습니다.
참조 반환값을 이용하면 메소드의 호출자로 하여금 반환받은 결과를 참조로 다룰 수 있도록 합니다.
이런 메소드를 선언하는 방법으로는 ref 한정자를 이용해 메소드를 선언하고, return 문이 반환하는 변수 앞에도 ref 키워드를 명시해야 합니다.
class SomeClass
{
int SomeValue = 10;
public ref int SomeMethod() // ref 키워드로 메소드를 한정합니다.
{
// 이런 일 저런 일
return ref SomeValue; // return 문을 사용할 때 ref 키워드를 반환할 필드나 객체 앞에 붙여줍니다.
}
}
SomeMethod()가 반환하는 결과를 호출자가 참조로 넘겨받고 싶다면 다음과 같이 결과를 잠는 지역 변수와 호출할 메소드의 이름 앞에 ref 키워드를 위치시켜야 합니다. 참고로 이렇게 참조로 반환받은 결과를 담는 지역 변수를 '참조 지역 변수'라고 부릅니다.
SomeClass obj = new SomeClass();
ref int result = ref obj.SomeMethod(); // result는 참조 지역 변수입니다.
[실습]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace ThisIsCSharpExam.Ch._06.MethodExam
{
class RefReturnExam
{
public void Main()
{
Product carrot = new Product();
// ref_local_price를 수정하면 carrot.price의 내용도 바뀝니다.
ref int ref_local_price = ref carrot.GetPrice();
int normal_local_price = carrot.GetPrice();
carrot.PrintPrice();
Console.WriteLine($"Ref Local Price :{ref_local_price}");
Console.WriteLine($"Normal Local Price :{normal_local_price}");
ref_local_price = 200;
carrot.PrintPrice();
Console.WriteLine($"Ref Local Price :{ref_local_price}");
Console.WriteLine($"Normal Local Price :{normal_local_price}");
}
}
class Product
{
private int price = 100;
public ref int GetPrice()
{
return ref price;
}
public void PrintPrice()
{
Console.WriteLine($"Price :{price}");
}
}
}
#6 출력 전용 매개변수
두 개 이상의 결과를 요구하는 특별한 메소드가 있습니다. 가령 덧셈이나 뺄셈을 구현한다면 두 개의 매개변수를 넘겨받아 하나의 결과를 만들어 반환할 수 있지만, 나눗셈을 구현할 때는 제수와 피제수를 매개변수로 넘겨받고 결과는 몫과 나머지 두 개로 반환할 필요가 있습니다.
다음과 같이 ref 키워드를 이용해서 메소드를 구현하면 몫과 나머지를 한 번에 반환할 수 있습니다.
void Divide(int a, int b, ref int quotient, ref int remainder)
{
quotient = a / b;
remainder = a % b;
}
그럼 이 메소드를 사용하면 아래와 같으며 잘 동작합니다.
int a = 20;
int b = 3;
int c = 0;
int d = 0;
Divide(a, b, ref c, ref d);
Console.WriteLine("Quotient : {0}, Remainder {1}", c, d);
이처럼 ref만으로도 여러 개의 결과를 메소드에서 얻어올 수 있지만, C#은 조금 더 안전한 방법으로 바로 out 키워드를 이용한 '출력 전용 매개변수'를 지원합니다.
out 키워드의 사용방법은 ref키워드 대신 out 키워드를 사용하는 것이 전부입니다.
out 키워드를 이용해서 매개변수를 넘길 때는 메소드가해당 매개변수에 결과를 저장하지 않으면 컴파일러가 에러 메시지를 출력합니다. 한편, 메소드를 호출하는 쪽에서는 초기화하지 않은 지역 변수를 메소드의 out 매개변수로 넘길 수 있습니다. 컴파일러가 호출당하는 메소드에서 그 지역 변수를 할당할 것을 보장하기 때문입니다.
이처럼 컴파일러를 통해 결과를 할당하지 않는 버그가 만들어질 가능성을 제거할 수 있다면, 우리는 그 방법을 사용해야 합니다. 그 이유는 런타임에 발생하는 버그는 컴파일 타임에 발생하는 버그보다 훨씬 잡기가 어렵기 때문입니다.(런타임 버그는 프로그래머의 논리력으로 추적해야 합니다.)
그리고 출력 전용 매개변수는 메소드를 호출하기 전에 미리 선언할 필요가 없으며, 호출할 때 매개변수 목록 안에서 즉석으로 선언하면 됩니다.
[실습]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ThisIsCSharpExam.Ch._06.MethodExam
{
class UsingOutExam
{
public void Main()
{
int a = 20;
int b = 3;
// int c;
// int d;
Divide(a, b, out int c, out int d);
Console.WriteLine($"a:{a}, b:{b}:, a/b:{c}, a%b:{d}");
}
public static void Divide(int a, int b, out int quotient, out int remainder)
{
quotient = a / b;
remainder = a % b;
}
}
}
'ZAION > C++' 카테고리의 다른 글
[이것이 C#이다]Ch.19 스레드와 태스크 - 2 (0) | 2024.02.02 |
---|---|
[이것이 C#이다]Ch.19 스레드와 태스크 - 1 (0) | 2024.02.01 |
[이것이 C#이다]Ch.05 코드의 흐름 제어하기 - 2 (0) | 2024.01.31 |
[이것이 C#이다]Ch.05 코드의 흐름 제어하기 - 1 (0) | 2024.01.30 |
[아키텍처] 2-tier/3-tier 개념 타파하기 (0) | 2024.01.30 |