Media Log


1. 메소드(Method)


이번 강좌에서는 메소드가 무엇인지, 메소드가 어떠한 기능을 하는지, 또 어떻게 쓰이는지 알아보도록 하겠습니다. C#에서의 메소드(Method)는, C언어와 C++의 함수(Function)와 비슷한 기능을 합니다. 메소드를 간단히 나타내자면, 이어지는 코드들을 묶어놓은 코드 블록입니다. 예를 들어서, 아래는 제곱 후 결과물을 출력하는 기능을 가진 메소드입니다.

...
    static void square(int a) {
        Console.WriteLine("{0}*{1}={2}", a, a, a*a);
    }
...

위 코드는, square라는 녀석에게 값을 넘겨주고, 임시로 a라는 변수에 값을 기억시킵니다. 그리고 이 a 변수를 가지고 제곱하여 출력하는 코드입니다. 대충 메소드를 어떻게 정의하는지 보이시나요? 아래는 C#에서 메소드를 정의하는 방법입니다.

[접근 지정자] 반환형식 메소드명(매개변수 목록) {
    // 실행될 코드
}

여기서 접근 지정자에는, private, public, protected 등이 있습니다. 말 그대로 접근 가능한 범위를 지정할 수 있으며, 접근 지정자에 대한 설명은 나중에 더 자세히 다룰 예정입니다. 반환 형식은 메소드의 결과에 따라 다릅니다. 반환되는 데이터의 형식이 int형이라면, int를 써야하며 double형이라면 double을 반환형식에다 적어주어야 합니다. 만약에 반환되는 값이 없을 경우에는 void를 적으시면 됩니다. 그리고 매개변수 목록은 메소드 호출 시 값을 넘길 수 있도록 하는 '매개변수'가 1개 이상 등장합니다.


아래의 예제는 반환형식의 이해를 돕기위한 예제입니다.

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}", Division(40, 10));
        }

        static int Division(int a, int b)
        {
            return a / b;
        }
    }
}
결과:
4
계속하려면 아무 키나 누르십시오 . . .

우선은 예제의 9행을 봅시다. 이상한 게 있다면 인자 부분에 변수나 어느 값이 아닌 Division라는 메소드가 등장했습니다. 15행의 Division라는 메소드를 살펴봅시다. 이 메소드는 두 정수를 매개변수로 받습니다. 우리는 40과 10이란 값을 각각 넘겼으므로, 40이란 값은 매개변수 a에 저장되고, 10이란 값은 매개변수 b에 저장됩니다. 그러곤 메소드가 이 a, b를 이용하여 결과를 만들어 호출부로 반환(return)합니다.

return 부분을 보자면, a가 40, b가 10이므로 결과는 4가 됩니다. 그러곤 return를 만나 4라는 값을 호출된 부분으로 반환합니다. 즉, 이 메소드를 불러냈던 곳으로 결과를 다시 보낸다는 말과 같습니다. 결과가 4이므로 Division(40, 10)은 4라는 값을 가지게 됩니다. 그래서 결과처럼 4라는 결과가 출력될 수 있었던 거죠. 그리고 한가지 주의할점이 있는데, return 키워드는 맨 마지막에서만 등장하는 것이 아니라 코드의 중간에서도 등장할 수도 있습니다. 또한, return 키워드를 만나게 되면 메소드에서 빠져나온다는 사실을 알고 계셔야 합니다. 

2. Call by value vs Call by reference

Call by value와 Call by reference가 무엇인지 설명하기전에, 예제를 한번 짚고 넘어갑시다. 아래 예제에서는 두 매개변수의 값을 교환하는 Swap란 함수가 등장합니다.
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 40;
            int b = 10;

            Console.WriteLine("Swap before: a={0}, b={1}", a, b);

            Swap(a, b);

            Console.WriteLine("Swap after: a={0}, b={1}", a, b);
        }

        static void Swap(int a, int b)
        {
            int temp = b;
            b = a;
            a = temp;
        }
    }
}
결과:
Swap before: a=40, b=10
Swap after: a=40, b=10
계속하려면 아무 키나 누르십시오 . . .

먼저, 코드를 보자면 19행에서 Swap란 함수가 등장합니다. Swap 함수의 내용을 보자면 값을 임시로 담아둘 temp란 변수를 선언하고 temp란 변수의 값을 b의 값으로 초기화시킵니다. 그러고는 b에 a의 값을 담습니다. 그리고 a에는 temp(원래 b의 값)을 담습니다. 즉, Swap 함수는 a와 b의 값을 서로 바꿔버리는 기능을 수행합니다. 여기서 이렇게 매개변수를 변수의 값으로 가져온 경우를 Call by value(복사에 의한 함수 호출)이라고 부릅니다. 

다시 9행으로 돌아가, 정수형 변수 a와 b가 선언되었으며 그와 동시에 40과 10으로 각각 초기화되었습니다. 그러곤 Swap 함수가 실행되기 전의 a, b의 값을 출력한 뒤에 Swap 함수가 실행되고 다시 한번 a, b의 값을 출력합니다. 그러나 Swap 함수가 실행됨에도 불구하고 a와 b의 값은 바뀌지 않습니다. 왜 값이 바뀌지 않았던 걸까요?

우리가 메소드로부터 변수를 넘겨줄 때부터, 매개변수 a와 b이 변수 a와 b를 가르키는 게 아니고, 그저 변수 a와 b의 값을, 매개변수 a와 b로 복사하는 것 뿐입니다. 한마디로 말하자면, 매개변수 a와 b, 변수 a와 b는 서로 별개이며, 다른 메모리 공간을 사용합니다. 그저 매개변수 a와 b의 값이 서로 바뀌었을 뿐, 변수 a와 b의 값은 그대로인 셈이죠. 그렇다면 변수 a와 b의 값을 바꾸도록 하려면 어떻게 해야할까요? 바로 Call by reference(참조에 의한 호출)로 넘긴다면 가능합니다. Call by reference는 Call by value와는 달리, 변수의 주소 값을 매개변수로 보냅니다. 직접 원래의 변수를 참조하는 방법입니다.

한번 위의 예제를 Call by reference로 넘겨보도록 하겠습니다.
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 40;
            int b = 10;

            Console.WriteLine("Swap before: a={0}, b={1}", a, b);

            Swap(ref a, ref b);

            Console.WriteLine("Swap after: a={0}, b={1}", a, b);
        }

        static void Swap(ref int a, ref int b)
        {
            int temp = b;
            b = a;
            a = temp;
        }
    }
}
결과:
Swap before: a=40, b=10
Swap after: a=10, b=40
계속하려면 아무 키나 누르십시오 . . .

아까의 예제와 비교해보면 ref라는 새로운 녀석이 등장합니다. ref 키워드는 매개변수를 참조형식으로 사용할 때 사용되는 키워드입니다. 메소드의 선언, 호출 부분에 ref 키워드를 달아주면 그게 곧 Call by reference로 넘겨주는 방법입니다. 결과를 살펴보면 a와 b의 값이 서로 바뀐 것을 확인하실 수 있습니다. 즉, 원본의 데이터의 값이 바뀐 것을 알 수 있습니다. 

3. 메소드 오버로딩(Method Overloading)

우리가 알고 있는 그대로 C언어에서는 오버로딩이 불가능했습니다. 메소드명을 중복하여 다르게 구현하는 것이 불가능했죠. 그런데 C#에선 메소드 오버로딩을 지원합니다. 같은 메소드명에 인자만 다르게 하여 매개변수의 데이터 형식, 수에 따라 그에 맞는 코드를 실행할 수 있게 됐습니다. 아래는 오버로딩의 예입니다.
..
    static int Add(int a, int b) {
        Console.WriteLine("두 int형 끼리의 덧셈");
        return a + b;
}

    static int Add(double a, double b) {
        Console.WriteLine("두 double형 끼리의 덧셈");
        return a + b;
    }

    static int Add(int a, int b, int c) {
        Console.WriteLine("세 int형 끼리의 덧셈");
        return a + b + c;
    }
..
위 예제를 보시면 같은 메소드명인데도 불구하고 여러 번 등장합니다. 이렇게 똑같은 메소드명인데도 불구하고 매개변수에 따라 호출되는 메소드가 다릅니다. 아래는 오버로딩의 예입니다.
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}", Add(50, 10));
            Console.WriteLine("{0}", Add(544.2, 63.2));
            Console.WriteLine("{0}", Add(4, 7, 9));
        }

        static int Add(int a, int b)
        {
            Console.WriteLine("두 int형 끼리의 덧셈");
            return a + b;
        }

        static double Add(double a, double b)
        {
            Console.WriteLine("두 double형 끼리의 덧셈");
            return a + b;
        }

        static int Add(int a, int b, int c)
        {
            Console.WriteLine("세 int형 끼리의 덧셈");
            return a + b + c;
        }
    }
}
결과:
두 int형 끼리의 덧셈
60
두 double형 끼리의 덧셈
607.4
세 int형 끼리의 덧셈
20
계속하려면 아무 키나 누르십시오 . . .

코드의 9행을 보시면, Add 메소드로 세 번이나 다른 형태의 값들을 전달하였음에도 불구하고 결과를 보시면 제대로 출력됨을 확인할 수 있습니다. 14행에 있는 Add 메소드는 두 정수가 넘어올 때 호출되고, 20행에 있는 Add 메소드는 두 실수가 넘어올 때 호출되며, 26행에 있는 Add 메소드는 세 정수가 넘어올 때 호출됩니다. 이처럼, 매개변수의 형식과 수에 따라 호출되는 메소드가 다름을 알 수 있습니다. 유용하죠?

4-1. ref

우리가 방금 넘어왔던 Call by reference 예제에서 ref 키워드를 한번 만났었죠? ref 키워드의 역할은 대충 알고 계시겠지만, 다시 한번 짚고 넘어가도록 하겠습니다. ref 키워드를 사용하면 변수의 값을 그대로 전달하는 게 아닌, 변수의 메모리 주소를 전달한다고 기억합시다. 아래는 ref 키워드가 사용된 예제입니다.
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 40;

            Add(ref a, 100);
            Console.WriteLine("a={0}", a);

            Add(ref a, 200);
            Console.WriteLine("a={0}", a);

            Add(ref a, 300);
            Console.WriteLine("a={0}", a);
        }

        static void Add(ref int a, int b)
        {
            a += b; // a = a + b;
        }
    }
}
결과:
a=140
a=340
a=640
계속하려면 아무 키나 누르십시오 . . .

코드를 보시면, ref 키워드가 메소드를 호출할때도 쓰였고 매개변수에서도 사용됬습니다. Add 메소드의 매개변수에 변수 a의 주소값을 넘겨주는 셈이죠. 실제 a의 주소를 안 Add 메소드는 a에다 매개변수 b의 값을 a에다 더합니다. 결과를 출력해봤더니, 정상적으로 덧셈이 진행됨을 알 수 있습니다. ref 매개변수를 사용하려면 메소드를 호출할때나, 메소드를 정의할때도 ref 키워드를 명시적으로 사용해주어야 합니다.

4-2. out

out 키워드는 ref 키워드와 비슷하게 인수를 참조로 전달할때 사용됩니다. 그러나 차이점이 존재합니다. out 키워드를 사용하면 변수를 전달하기전 초기화해야하는 ref 키워드와는 달리 초기화 하지 않고도 전달이 가능합니다. 아래는 out 키워드의 사용 예제입니다.
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a;

            Add(out a);
            Console.WriteLine("a={0}", a);
        }

        static void Add(out int a)
        {
            a = 100;
        }
    }
}
결과:
a=100
계속하려면 아무 키나 누르십시오 . . .

12행을 보시면 정수형 변수 a가 등장하나 초기화는 되지 않은것을 확인할 수 있습니다. 14행에서는 Add 메소드가 등장하고 매개변수로 변수 a의 주소값을 넘깁니다. 18행에서의 Add 메소드에서, 이 a의 주소값을 가지고 a에 접근하게 됩니다. a에 100이란 값을 대입합니다. 그러면 a가 가지고 있는 값은 100이 되죠. 메소드를 빠져나와 a의 결과를 출력해보면 성공적으로 a의 값이 100이 됨을 알 수 있습니다.

4-3. params

우리가 만약, 길이에 제한받지 않고 수를 넘겨주어 그 수의 총 합을 구하고 싶을때는 어떻게 하면 될까요? 바로 params 키워드를 사용하면 쉽게 구할 수 있습니다. params 키워드의 기능은 메소드에 여러개의 값을 전달할 수 있도록 도와줍니다. 아래는 params 키워드가 사용된 예제입니다.
using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("sum={0}", total(20, 10, 40, 4, 7, 6, 44, 55, 2));
            Console.WriteLine("sum={0}", total(30, 4, 5));
        }

        static int total(params int[] list)
        {
            int sum = 0;

            for (int i = 0; i < list.Length; i++)
                sum += list[i];

            return sum;
        }
    }
}
12~13행의 코드를 보시면 길이에 제한 없이, 값을 total 메소드로 넘겨줍니다. 그러면, 16행의 total 메소드를 살펴보도록 합시다. 매개변수 부분에 params 키워드가 사용됬고 int형 뒤에 []가 붙었습니다. 여기서 params 키워드 말고도 배열이란 개념이 사용되었으나, 우리는 아직 배열을 배우지 않았으므로 간단히 이해하고 넘어가도록 하겠습니다. 배열(Array)은 변수들이 줄지어 서있는 것과 같다고 생각하시면 됩니다. 변수들의 모임이죠. 즉, int[]는 정수형 배열을 의미합니다. 우리가 total 메소드로 20, 10, 40, 4, 7, 6, 44, 55, 2를 넘김으로써 list에는,
list = {20, 10, 40, 4, 7, 6, 44, 55, 2}
위와 같이 저장되게 됩니다. 그러고 이 list를 가지고 하나하나 접근하여 sum이란 변수에 더하고 반복문을 빠져나와 sum의 값을 반환(return)합니다. for문을 잠깐 살펴보자면, list.Length는 배열 list의 길이를 구해주는 역할을 합니다. 그리고 for문 내를 보면 list[i]처럼 배열에 들어있는 각 요소에 접근할 경우에는 첨자값(index, 인덱스)값으로 접근합니다. 아직은 배열을 배우지 않아 이해가 안되실지도 모르겠지만, 여기서 중요한건 params 키워드를 사용하여 매개변수의 길이를 유연하게 만들수 있음을 아셔야 합니다. 

오늘의 강좌는 여기서 마치도록 하겠습니다. 수고하셨습니다.

다음 강좌에서는 배열(Array)에 대해 설명합니다.


  1. 김승현 at 2012.10.20 00:39 신고 [edit/del]

    감사합니다~

    Reply
  2. 데오늬 at 2012.11.30 17:17 신고 [edit/del]

    좋은 강좌 잘보고 갑니다~

    Reply
  3. 황순표 at 2012.12.06 10:51 신고 [edit/del]

    유용하게 잘 보고 있습니다. 쉽지 않은 작업일텐데..감사합니다.

    Reply
  4. C#고고 at 2013.03.21 14:49 신고 [edit/del]

    처리되지 않은 형식이라면서 에러가 나네요 ㅠ.ㅠ 어떡해 해야되는건지;;;
    params 문에서 그러네요 ㅠ.ㅠ

    Reply
  5. 외톨이 혁명전쟁 at 2013.03.31 09:09 신고 [edit/del]

    근데 Division메소드는 오버로딩이 안됩니다. 왜 그런가요? 그리고 오버로딩이 가능한 메소드는 또 무엇이 있는지 궁금합니다.

    Reply
    • BlogIcon EXYNOA at 2013.03.31 12:30 신고 [edit/del]

      Division 메서드도 오버로딩 됩니다. 오버로딩 되고 안되는 메서드는 따로 존재하지 않습니다. 어떻게 작성하셨는지 보여주세요.

  6. smfinety at 2013.06.12 17:08 신고 [edit/del]

    감사...잘 배우고 있습니다

    Reply
  7. ZiNee at 2013.10.14 00:22 신고 [edit/del]

    params 키워드는 참으로 유용하네요. 강좌 감사합니다.

    Reply
  8. 이시도르 at 2015.01.28 18:18 신고 [edit/del]

    소중한 강의 감사합니다.
    문제는 프로그래밍 초짜인 제가 메모리 주소개념에서 좀 헷갈리네요 ㅎㅎ

    Reply
    • BlogIcon EXYNOA at 2015.11.13 15:55 신고 [edit/del]

      답글을 늦게 달아드리게 되어서 죄송하다고 말씀드리고 싶습니다. 메모리 주소 개념 중 어느 부분이 헷갈리시는지 말씀해주시면 그 부분을 중심으로 더 자세히 설명하는 방향으로 나가겠습니다.

  9. 중요중요 at 2015.10.14 20:35 신고 [edit/del]

    이부분은 중요한 것들이 많이 나오네요.
    외울 때까지 복습해야겠어요!

    Reply
  10. 광영 at 2017.04.18 10:43 신고 [edit/del]

    ref키워드와 out 키워드가 좀 까다롭네요. ref 자체는 *와 &를 단순화 시켜 놓은 좋은 아이디어라고 생각하고 이해할 수 있었는데, out 은 대체 왜..... 시험삼아서 int a; function(ref a); static void fun(ref int a){a=100;} 하니까 에러 토해내더군요. 그러면서 할당되지 않은 변수를 쓰려 하지 마라 라고 하는데, 이게 그렇다면 값을 넣을 때 비로소 할당된다 라고 이해하면 되는 걸까요? 파이썬마냥 모든걸 다 객체로 봐서 쓸 때 할당하려고 하는 것인지 참 헷갈리네요.

    Reply
  11. at 2017.11.08 17:01 신고 [edit/del]

    강의 잘 보고 있습니다..
    return의 개념에 대해 궁금해서 댓글 남깁니다..
    처음 메소드를 설명해주실때 void를 없애서 return값을 설정해서
    Division을 호출했던곳으로 돌아간거까지는 이해를 했습니다.
    다음 Call by value vs Call by reference 설명 부분에서는
    ref의 역할을 설명이 중점이 되어 있는데 이 부분에서
    Swap 메서드에는 void로 리턴값이 없음에도 불구하고
    a,b가 스왑된 값이 main함수의 Swap 호출부분에 적용이 된걸 볼수있습니다. 그 아래부분들도 다 비슷한 케이스구요..
    return값이 없어도 값이 전달되었다는 의미인거 같은데
    어떻게 이해하는게 좋을지 댓글 부탁드립니다..

    Reply
    • BlogIcon EXYNOA at 2017.11.10 05:07 신고 [edit/del]

      변수마다 자신만의 메모리 공간을 가지고 있습니다. 값에 의한 호출(call by value)의 경우에는 메소드로 값을 넘기면, 그 값이 메소드의 매개변수에 복사되는 것입니다.

      복사된 이후론, main에 있는 지역변수 a, b와 Add 메소드의 매개변수 a, b는 별개의 메모리 공간을 사용합니다. 그렇다면, 함수 내에서 매개변수를 가지고 아무리 값을 수정해도 원래 변수에는 영향을 미치지 않습니다.

      반면에, 참조에 의한 호출(call by reference)와 같은 경우에는 값에 의한 호출과는 달리 main에 있는 지역변수 a, b를 매개변수 a, b를 통해 간접적으로 참조합니다. 이렇게 되면 복사본이 아닌 원본을 수정하는 것이므로, 결과에 영향을 미치게 되는 것입니다.

  12. swap 질문 at 2018.02.06 15:02 신고 [edit/del]

    swap 에서
    우리가 메소드로부터 변수를 넘겨줄 때부터, 매개변수 a와 b이 변수 a와 b를 가르키는 게 아니고, 그저 변수 a와 b의 값을, 매개변수 a와 b로 복사하는 것 뿐입니다. 한마디로 말하자면, 매개변수 a와 b, 변수 a와 b는 서로 별개이며, 다른 메모리 공간을 사용합니다. 그저 매개변수 a와 b의 값이 서로 바뀌었을 뿐, 변수 a와 b의 값은 그대로인 셈이죠. 그렇다면 변수 a와 b의 값을 바꾸도록 하려면 어떻게 해야할까요

    여기서 그럼 매개변수와 변수의 값이 같으니 swap 함수를 사용해도 바뀐 값이 아닌 같은 값이 나온다는 설명인가요?...

    출처: http://blog.eairship.kr/135 [누구나가 다 이해할 수 있는 프로그래밍 첫걸음]

    Reply
  13. df at 2018.02.25 21:13 신고 [edit/del]

    강의 잘 보고있습니다.
    이해가 잘되네요..

    지금까지 잘 따오다가 슬슬 어려워지네요,, 메소드 ㅠㅠ
    감사합니다..
    혹시 강의 업로드해주신 것 외에 추가 정보가 있으면 업로드해주시면 감사히 보겠습니다.
    덕분에 잘 배우고 있습니다. 감사합니다.

    Reply
  14. 입문 at 2018.05.03 09:20 신고 [edit/del]

    C# 이제 막 입문해볼려고 하는데, 입문자가 보기에는 쉽지 않네요.
    아무래도 using, namespace, static, void, 메소드 등.. 이런 개념을 정확히 잡혀있지 않으니, 일단 기본기부터 공부한 후에 몇달뒤에 이걸 보면 아주 좋은 강좌가 될거 같습니다.

    Reply

submit

티스토리 툴바