[C# 기초] 3. 메소드(Method)


C와 C++에서는 함수(Function)이라고 불렸고 C#에서는 메소드(Method)라고 불립니다. 이 메소드(Method)에 대해 배웁니다.

메소드 (Method)


객체지향 프로그래밍 언어에서 사용하는 용어로 C와 C++에서는 함수(Function) 이라 불렸고, 파스칼에서는 프로시져(Procedure)라고 불렸습니다. 또는 서브루틴(Subroutine), 서브프로그램(Subprogram)이라고 부르기도 합니다. 메소드는 클래스 안에 종속되어 존재한다는 것인데, C#은 모두 클래스로 이루어졌기 때문에 메소드라고 부르면 됩니다.

함수는 어떤 입력 수학적으로 말하는데 이것과 같이 C#에서도 메소드의 입력을 매개변수(Parameter)라고 부릅니다. 메소드는 함수라고 생각하셔도 무방합니다.

참조에 의한 매개변수 전달 (Call by reference)


한글로 표현하면 생소한 용어때문에 어려워 보이지만 개념은 어렵지 않습니다. 메소드에 입력을 전달할 때 실제 데이터 주소를 전달해줄 것인지(Call by reference), 데이터의 값을 전달할 것인지(Call by value)를 구분한 것 뿐입니다.

구분한 이유는 데이터의 값을 전달할 경우 실제 값이 입력으로 들어가는 것이 아니라 값이 복사되어 입력값으로 전달됩니다.(실제 데이터의 메모리 주소는 다릅니다). 이때는 함수에서 전달받은 값을 아무리 변경해도 원본이 유지됩니다. 하지만 데이터의 주소를 전달해주게 되면 데이터의 주소를 접근하기 때문에 실제 데이터가 변경될 수 있습니다.

이러한 참조에 의한 매개변수 전달(Call by reference)은 함수에서 실제 입력값을 변경해야 할 때 사용합니다. 예를 들어 두 개의 데이터의 값을 서로 맞바꾸는 Swap 메소드를 만들어 보겠습니다.

예제 코드:

class Program
{
    static void Swap(ref int x, ref int y)
    {
        int temp = x;
        x = y;
        y = temp;
    }

    static void Main(string[] args)
    {
        int x = 3;
        int y = 5;
        Swap(ref x, ref y);
        Console.WriteLine("{0} , {1}", x, y); // 5, 3
    }
}

위의 함수는 입력값이 실제로 변경되어야 하기 때문에 ref 키워드를 붙여서 Call by reference를 사용하여 변경할 수 있었습니다. C# 에서는 Call by reference는 ref 키워드를 붙여 전달할 수 있습니다.

(C언어에서의 Call by reference) C에서는 포인터를 이용하여 데이터의 주소를 전달할 수 있습니다. C#에서는 프로그래머가 메모리를 참조한다는 것 자체가 위험하다고 판단하였기 때문에 포인터를 사용할 수 없게 했습니다. (포인터를 사용할 수 있는 방법이 있긴 합니다..)

(static 키워드가 있는 이유) 참고로 C#에서는 모든 것이 클래스로 이루어져 있습니다. C, C++ 등에서 최초로 시작되는 Main함수도 클래스로 이루어져 있는 것이 특징입니다. 이 Main함수를 시작하자마자 호출하기 위해 static 키워드로 정적으로 존재합니다. 이 예제코드에서 Main에서 Swap메소드를 사용하기 위해서 Swap메소드도 static으로 선언하였습니다.

출력 전용 매개변수, out


일반적인 함수들은 Return으로 하나의 값만 반환합니다. 하지만 2개 이상의 반환값을 갖는 함수는 어떻게 구현할까요?

참조에 의한 매개변수 전달(Call by reference)로도 2개 이상의 반환 값을 가지는 것이 가능합니다.

예를 들어 어떤 두 숫자를 가지고 몫과 나머지를 동시에 반환하고 싶을 때는 다음과 같이 함수를 만들면 됩니다.

void Divide (int a , int b, ref int quotient, ref int remainder)
{
	quotient = a/b;
	remainder = a%b;
}

하지만 C#은 out 키워드를 이용한 출력전용 매개변수를 제공해 더욱 안전한 방법으로 사용할 수 있습니다.

예제코드:

class Program
{
    static void Divide(int x, int y, out int quotient, out int remainder)
    {
        quotient = x / y;
        remainder = x % y;
    }

    static void Main(string[] args)
    {
        int x = 5;
        int y = 3;
        int quo, rem;
        Divide(x, y, out quo, out rem);
        Console.WriteLine("quo: {0} , rem: {1}", quo, rem); 
        // quo : 1, rem : 2
    }
}

out 키워드를 사용할 경우 장점:

  1. 메소드가 out 변수에 값을 저장하지 않을 경우 컴파일러가 에러를 발생합니다.
  2. 호출된 메소드에서는 입력된 out 매개변수를 읽을 수 없고, 오직 쓰기만 가능합니다.(다른 용도로 사용되는 것을 금지합니다)

컴파일러가 에러를 발생시키는 것은 큰 장점입니다. 그 이유는 컴파일 에러는 몇번째 line이 문제가 있음을 바로 알고 해결할 수 있습니다. 하지만 런타임 오류는 실제 프로그램이 실행되면서 어떤 상황에 에러를 발생하기 때문에 디버깅을 해야 찾아낼 수 있습니다.

오버로딩(Method Overloading)


메소드 오버로딩은 하나의 메소드 이름에 여러가지 구현을 overloading(과적)하는 것입니다. 비슷한 기능을 하는 여러가지 메소드에 각각의 이름을 붙이는 것은 어렵고, 비효율적인 일입니다.

오버로딩을 할 경우 비슷한 기능을 하는 메소드를 매개 변수의 자료형이나 개수 등을 보고 여러가지 메소드 중에서 컴파일을 할때 컴파일러가 실행할 메소드를 선택하기 때문에, 프로그램의 성능 저하도 없을 뿐더러 한가지 메소드 이름으로 비슷한 기능의 메소드를 전부 묶을 수가 있습니다.

예를 들어, 더하는 기능을 가진 Add라는 메소드를 보겠습니다.

int Add(int n1, int n2)
{
    return n1 + n2;
}

int Add(int n1, int n2, int n3)
{
    return n1 + n2 + n3;
}

double Add(double n1, double n2)
{
    return n1 + n2;
}

위와 같이 Add라는 메소드는 수없이 많이 만들어질 수 있는데 메소드 이름을 하나로 만들고 실제로 호출할 때도 각각에 맞는 입력을 넣어주기만 하면 됩니다. 이러한 방식을 오버로딩이라고 합니다.

가변길이 매개변수, params


같은 기능을 가졌으나 매개 변수의 개수만 다른 메소드를 오버로딩 하고 싶을 때 사용할 수 있습니다.

메소드 오버로딩을 이용할 수도 있겠지만, 가변길이 매개변수는 수만 다른 경우 갯수만큼 메소드를 오버로딩 하는 것보다 한번에 형식은 같지만 변수의 개수가 정해져있는 것이 아닐 경우 가변길이 매개변수가 유용하게 쓰일 수 있습니다. 매개변수 앞에 ‘params’라는 키워드를 사용합니다.

예를 들면 모든 수를 합하는 Sum 메소드를 가변길이 매개변수를 이용하여 만들어 보겠습니다.

예제코드:

class Program
{
    static int Sum(params int[] args)
    {
        int sum = 0;
        foreach(int num in args)
        {
            sum += num;
        }
        return sum;
    }

    static void Main(string[] args)
    {
        int result1 = Sum(2, 3, 5);
        int result2 = Sum(2, 3, 5, 7);
        int result3 = Sum(2, 3, 5, 7, 9);
        Console.WriteLine("result1 : {0} result2: {1} result3: {2}", result1, result2, result3);
        // result1 : 10 result2: 17 result3 : 26
    }
}

주의할 점은 순서에 상관없이 매개변수 이름에 할당하기 때문에 하나라도 사용할 경우 전부 명명된 매개변수로 값을 할당해줘야 합니다.

명명된 매개변수(Named Parameter)


메소드를 호출할때 매개변수에 데이터를 넣을때 일반적으로 순서대로 넣습니다.

예를 들면 void Method(int a, int b, int c); 라는 메소드가 정의되어 있을때 Method(1,2,3); 로 호출하면 a,b,c 차례대로 값이 1,2,3이 들어가서 Method 기능을 수행합니다.

이렇게 함수가 어떤 매개변수를 어떤 순서로 넣어야할지 알아야 하는데, C#에서는 조금 더 편리하게 매개변수에 이름을 직접 명명하여 데이터를 전달할 수 있습니다. 매개 변수가 너무 많은 메소드를 사용할 때 어느 특정 매개변수에 데이터를 전달할때 유용하게 사용할 수 있습니다.

class Program
{
    static void ShowProfile(string name, int age)
    {
        Console.WriteLine("이름: {0}  나이: {1}", name, age);
    }

    static void Main(string[] args)
    {
        ShowProfile("철수",10); // 이름: 철수 나이: 10
        ShowProfile(name: "영희", age: 15); // 이름 : 영희 나이: 15
        ShowProfile(age: 20, name: "영수"); // 이름: 영수 나이: 20
    }
}

선택적 매개변수(Default parameter)


메소드를 정의할 때 매개변수에 디폴트 값을 넣어 매개변수를 전달하지 않을 경우 디폴트 값이 들어오도록 할 수 있습니다. 매개변수가 많은 메소드에서 넣고 싶은 매개변수만 선택적으로 넣을 수도 있습니다.

주의할 점은 메소드 오버로딩과 선택적 매개변수를 동시에 사용할 때의 모호함이 있으므로 같이 사용하면 안됩니다.

void Method(string str1="", string str2="");
void Method(string str1);

위와 같은 두 가지 메소드가 있을 경우 Method(“abc”); 로 메소드 호출 할때 여러분이 컴파일러라면 어떤 함수를 호출해야할 지 판단이 되시나요?


메소드를 여러 방식으로 다루는 것을 정리하였는데 한글로 표현하기 어려워 용어도 참 낯설게 느껴집니다. 여기서 설명한 모든 것들을 이해할 필요는 없고, 이런 것도 있구나 하면서 읽고 넘어가셔도 문제가 없다고 생각합니다. 실제 개발하다가 필요한 상황이 생겼을 때 다시 참고하시기 바랍니다.