[이것이 C#이다] 6. 메소드

ssu_hyun·2022년 4월 11일
0

C#

목록 보기
10/22

Key point

  • 메소드
  • return문
  • 매개변수
  • 메소드 오버로딩
  • 매개변수 옵션

6.1 메소드(Method)

  • 일련의 코드를 하나의 이름 아래 묶은 것

6.1.1 메소드 선언

  • 클래스 내부에 선언
  • 메소드 선언 필수 요소
    • 반환 형식
    • 메소드 이름
    • 매개변수 목록
  • 선언 형식
    class [클래스 이름]
    {
    	[한정자] [반환 형식] [메소드 이름]([매개변수 목록])
       {
       	// 실행하고자 하는 코드 1
           // 실행하고자 하는 코드 2
           // ...
           // 실행하고자 하는 코드 n
           
           return [메소드 결과]  // 메소드 결과 형식 = 반환 형식
       }
    }
  • 메소드 구조
  • 메소드 호출 시 일어나는 프로그램 흐름의 변화
// Calculator //

using System;

namespace Method
{
	class Calculator
    {
    	public static int Plus(int a, int b) // 매개변수 a, b
        // static : Calculator의 인스턴스를 만들지 않고도 해당 메소드를 호출할 수 있다.
        {
        	return a+b;
        }
        
        public static int Minus(int a, int b) // 매개변수 a, b
        // static : Calculator의 인스턴스를 만들지 않고도 해당 메소드를 호출할 수 있다.
        {
        	return a-b;
        }
    }
 
 	class MainApp
    {
    	puublic static void Main()
        
        {
        	int result = Calculator.Plus(3, 4); // 인수 3, 4
            Console.WriteLine(result);
            
            result = Calculator.Minus(5, 2); // 인수 5, 2
            Console.WriteLine(result);
        }
    }
}

6.2 return

  • 점프문의 한 종류로 언제든지 메소드 중간에 호출되어 메소드를 종결시키고 프로그램의 흐름을 호출자에게 돌려줄 수 있다.
  • 반환형식과 일치하는 데이터를 반환해야 함
  • 메소드가 void 형식인 경우 반환값 없이 사용 가능
using System;

namespace Return
{
    class MainApp
    {
        static int Fibonacci(int n)
        {
            if (n < 2)
                return n;
            else
                return Fibonacci(n - 1) + Fibonacci(n - 2); // 반환 형식 int
        }

        static void PrintProfile(string name, string phone)
        {
            if (name == "")
            {
                Console.WriteLine("이름을 입력해주세요.");
                return; // 반환 형식 void이므로 아무 결과도 반환하지 않아도 됨
            }

            Console.WriteLine("Name:{0}, Phone:{1}", name, phone);
        }

        static void Main(string[] args)
        {
            Console.WriteLine("10번째 피보나치 수 : {0}", Fibonacci(10));

            PrintProfile("", "123-4567");
            PrintProfile("박상현", "456-1230");
        }
    }
}


6.3 매개변수(Parameter)

  • 메소드가 호출자에게서 전달받은 값(인수의 데이터)을 받는 변수
  • 메소드가 인수를 전달받으면 인수는 메소드 안으로 들어가는 것이 아니라 자신의 값을 매개변수에 "복사"하여 완전히 별개의 메모리 공간을 사용한다. 즉 데이터의 복사가 이루어지는 것.이처럼 메소드를 호출할 때 데이터를 복사해서 매개변수에 넘기는 것(매개변수가 변수나 상수로부터 값을 복사하는 것)을 "값에 의한 전달(pass by value)"이라 부른다.
using System;

namespace SwapByValue
{
    class MainApp
    {
        public static void Swap(int a, int b)
        {
            int temp = b;
            b = a;
            a = temp;
        }

        static void Main(string[] args)
        {
            int x = 3;
            int y = 4;

            Console.WriteLine("x:{0}, y:{1}", x, y);

            Swap(x, y);
            
            Console.WriteLine("x:{0}, y:{1}", x, y);
            /* 메소드는 호출시 인수의 값을 복사해 매개변수에 전달하므로 
            Swap 메소드 안에서 두 매개변수(a, b)를 바꿔도 이는 복사된 값이므로
            x, y에 아무런 영향을 주지 못한다. */
            
        }
    }
}


6.4 참조에 의한 매개변수 전달

  • 참조에 의한 전달(pass by reference) : 매개변수가 메소드에 넘겨진 원본 변수를 직접 참조

  • 메소드 선언과 호출 시 ref 키워드를 매개변수 앞에 붙여주면 됨

using System;

namespace SwapByRef
{
    class MainApp
    {
        static void Swap(ref int a, ref int b) // 선언 : ref + 매개변수
        {
            int temp = b;
            b = a;
            a = temp;
        }

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

            Swap(ref x, ref y); // 호출 : ref + 매개변수

            Console.WriteLine("x:{0}, y:{1}", x, y);
            // 매개변수가 값을 복사하는 것이 아닌 변수를 참조하므로 결과가 변함
        }
    }
}


6.5 참조 반환값 (ref return)

  • 호출자로 하여금 반환받은 결과를 참조로 다룰 수 있도록 한다.

  • 선언 방법

    class SomeClass
    {
    	int SomeValue = 10;
       
       public ref int SomeMethod() // ref 한정자로 메소드 선언
       {
       	//이런 일 저런 일
           return ref SomeValue; // return문이 반환하는 변수 앞 ref 명시
       }
    }
  • 참조 반환 메소드의 결과를 참조로 넘겨받고 싶다면 호출 시에도 ref 키워드를 사용해야 한다. 그렇지 않으면 평범한 메소드처럼 값의 반환이 이루어진다.

    • 참조 지역 변수(ref local) : 참조로 반환받은 결과를 담는 지역 변수
    // 값으로 반환하는 경우 (평범한 메소드)
    SomeClass obj = new SomeClass();
    int result = obj.SomeMethod();
    
    // 참조로 반환하는 경우 (참조 반환값)
    SomeClass obj = new SomeClass();
    ref int result = ref obj.SomeMethod(); // result = 참조 지역 변수
    
  • 예제 프로그램

    using System;
    
    namespace RefReturn
    {
       class Product
       {
           private int price = 100;
    
           public ref int GetPrice()
           {
               return ref price;
           }
    
           public void PrintPrice()
           {
               Console.WriteLine($"Price :{price}");
           }
       }
       
       class MainApp
       {
           static void Main(string[] args)
           {
               Product carrot = new Product();
               ref int ref_local_price = ref carrot.GetPrice(); // 참조 : ref_local_price를 수정하면 carrot.price의 내용도 바뀜
               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($"Local Price :{ref_local_price}");
               Console.WriteLine($"Normal Local Price :{normal_local_price}");
           }
       }
    }


6.6 출력 전용 매개변수

  • return(반환값 1개)과 달리 복수개의 값 반환

  • out키워드로 한정한 매개변수에 결과를 입력해야 함

  • ref키워드와 비슷하지만, 참조로 인수 전달을 하는 경우(ref)와 달리 인수의 값이 수정되지 않으면 컴파일러가 에러를 출력

  • 선언 방법 : 메소드의 선언부와 호출부에 out 키워드 사용

    // 선언
    void Divide(int a, int b, out int quotient, out int remainder)
    {
    	quotient = a / b;
        remainder = a % b;
    }
    
    // 호출
    int a = 20;
    int b = 3;
    // int c;
    // int d;
    /*출력 전용 매개변수는 메소드를 호출하기 전에 미리 선언할 필요없이
    호출할 때 매개변수 목록 안에서 즉석으로 out을 선언해도 된다.
                        ↓                               */
    Divide(a, b, out c, out d);
    
    Console.WriteLine("Quotient : {0}, Remainder {1}", c, d);
  • 예제 프로그램

    using System;
    
    namespace UsingOut
    {
       class MainApp
       {
           static void Divide(int a, int b, out int quotient, out int remainder)
           {
               quotient = a / b;
               remainder = a % b;
           }
    
           static void Main(string[] args)
           {
               int a = 20;
               int b = 3;
    
               Divide(a, b, out int c, out int d); // 출력 전용 매개변수 즉석 선언
    
               Console.WriteLine("a:{0}, b:{1}:, a/b:{2}, a%b:{3}", a, b, c, d);
           }
       }
    }


6.7 메소드 오버로딩

  • 하나의 메소드 이름에 여러 버전의 (메소드) 구현을 연결 및 축적하는 것

  • 오버로딩을 해놓으면 컴파일러는 오직 매개변수의 수와 형식만을 분석해 어떤 버전이 호출되어야 하는지를 찾아준다. 실행할 메소드의 버전을 찾는 작업은 컴파일 타임에 이루어지므로 성능 저하는 신경 쓸 필요가 없다.

  • 예제 프로그램

    using System;
    
    namespace Overloading
    {
       class MainApp
       {
           static int Plus(int a, int b)
           {
               Console.WriteLine("Calling int Plus(int,int)...");
               return a + b;
           }
    
           static int Plus(int a, int b, int c)
           {
               Console.WriteLine("Calling int Plus(int,int,int)...");
               return a + b + c;
           }
    
           static double Plus(double a, double b)
           {
               Console.WriteLine("Calling double Plus(double,double)...");
               return a + b;
           }
    
           static double Plus(int a, double b)
           {
               Console.WriteLine("Calling double Plus(int, double)...");
               return a + b;
           }
    
           static void Main(string[] args)
           {
           	   // 컴파일러가 입력된 매개변수를 분석해 알아서 적합한 메소드 버전을 찾아준다.
               Console.WriteLine(Plus(1, 2));
               Console.WriteLine(Plus(1, 2, 3));
               Console.WriteLine(Plus(1.0, 2.4));
               Console.WriteLine(Plus(1, 2.4));
           }
       }
    }
    

6.8 가변 개수 인수

  • 개수를 유연하게 조절할 수 있는 인수
  • params키워드와 배열을 이용하여 선언

메소드 오버로딩 & 가변 개수 인수

  • 메소드 오버로딩 : 매개변수의 개수뿐 아니라 형식이 다른 경우 사용
  • 가변 개수 인수 : 형식은 같으나 인수의 개수만 유연하게 달라질 수 있는 경우 사용
  • 예제 프로그램

    using System;
    
    namespace UsingParams
    {
       class MainApp
       {
           static int Sum(params int[] args) // 가변 개수 인수 선언
           {
               Console.Write("Summing... ");
               
               int sum = 0;
    
               for(int i=0; i<args.Length; i++)
               {
                   if (i > 0)
                       Console.Write(", ");
    
                   Console.Write (args[i]);
    
                   sum += args[i];
               }
               Console.WriteLine();
    
               return sum;
           }
           
           static void Main(string[] args)
           {
               int sum = Sum(3, 4, 5, 6, 7, 8, 9, 10); // 호출
    
               Console.WriteLine("Sum : {0}", sum);
           }
       }
    }
    


6.9 명명된 인수(Named Argument)

  • 메소드를 호출할 때 순서가 아닌 이름에 근거해 데이터를 할당할 수 있는 기능

  • 호출할 때만 인수의 이름 뒤에 콜론(:)을 붙인 뒤 그 뒤에 할당할 데이터를 넣어주면 됨

  • 예제 프로그램

    using System;
    
    namespace NamedParameter
    {
       class MainApp
       {
           static void PrintProfile(string name, string phone)
           {
               Console.WriteLine("Name:{0}, Phone:{1}", name, phone); 
           }
    
           static void Main(string[] args)
           {	
               // 인수 이름 : 데이터
               PrintProfile(name: "박찬호", phone: "010-123-1234");
               PrintProfile(phone: "010-987-9876", name: "박지성");
               PrintProfile("박세리", "010-222-2222");
               PrintProfile("박상현", phone:"010-567-5678");
           }
       }
    }
    


6.10 선택적 인수

  • 입력해도 되고 안 입력해도 되는 인수

  • 입력되지 않는 경우를 대비해 매개 변수에 기본 값을 할당해야 한다.

  • 필수 인수와 같이 쓰는 경우 반드시 필수 인수 뒤에 적어야 한다.

  • 예제 프로그램

    using System;
    
    namespace OptionalParameter
    {
       class MainApp
       {
           static void PrintProfile(string name, string phone = "")
           {
               Console.WriteLine("Name:{0}, Phone:{1}", name, phone);
           }
    
           static void Main(string[] args)
           {
               PrintProfile("중근");
               PrintProfile("관순", "010-123-1234");
               // 어느 매개변수에 어떤 인수를 할당했는지 잘 모르겠다면 명명된 인수 활용
               PrintProfile(name: "봉길");
               PrintProfile(name: "동주", phone:"010-789-7890");
           }
       }
    }


6.11 로컬 함수(Local Function)

  • 메소드 안에서 선언되고, 선언된 메소드 안에서만 사용되는 특별한 함수

  • 선언 방법

    class SomeClass
    {
    	public void SomeMethod()  // 메소드 선언
       {
       	int count = 0;
           SomeLocalFunction(1, 2);  // 로컬 함수 호출
           SomeLocalFunction(3, 4);
           
           void someLocalFunction(int a, int b)  // 로컬 함수 선언
           {
           	// Do Some Work
               Console.WriteLine($"count : {++count}");  // 로컬 함수는 자신이 속한 메소드의 지역 변수를 사용할 수 있다.
           }
       }
    }
  • 예제 프로그램

    using System;
    
    namespace LocalFunction
    {
       class MainApp
       {
           static string ToLowerString(string input)  // 메소드 선언
           {
               var arr = input.ToCharArray();
               for(int i=0; i<arr.Length; i++)
               {
                   arr[i] = ToLowerChar(i);  // 로컬 함수 호출
               }
    
               char ToLowerChar(int i)  // 로컬 함수 선언
               {
                   if (arr[i] < 65 || arr[i] > 90) // A~Z의 ASCII 값 : 65 : 90
                       return arr[i];  // ToLowerString() 메소드의 지역변수 arr 사용
                   else // a~z의 ASCII 값 : 97 : 122
                       return (char)(arr[i] + 32);  // ToLowerString() 메소드의 지역변수 arr 사용
               }
    
               return new string(arr);
           }
           static void Main(string[] args)
           {
               Console.WriteLine(ToLowerString("Hello!"));
               Console.WriteLine(ToLowerString("Good Morning."));
               Console.WriteLine(ToLowerString("This is C#."));
           }
       }
    }
    


연습 문제

  1. 다음 코드에서 Square() 메소드를 구현해 프로그램을 완성하세요. Square() 함수는 매개변수를 제곱하여 반환합니다. 프로그램의 실행 예는 다음과 같습니다.

    수를 입력하세요 : 3
    결과 : 9
    
    수를 입력하세요 : 34.2
    결과 : 1169.64
using System;

namespace Ex6_1
{
	class MainApp
    {
    	static double Square(double arg)
        {
        	// 이 메소드를 구현해주세요.
        }
        
        static void Main(string[] args)
        {
        	Console.Write("수를 입력하세요 : ");
            string input = Console.ReadLine();
            double arg = Convert.ToDouble(input);
            
            Console.WriteLine("결과 : {0}", Square(arg));
        }
    }
}
  1. 다음 코드에서 Mean() 메소드를 실행한 후의 mean은 얼마의 값을 가질까요? 3이라고요? 아닙니다. 0입니다. 자, 문제 나갑니다. mean이 0을 갖게 되는 원인은 무엇이며, 이를 바로잡으려면 다음 코드에서 어떤 부분을 고쳐야 할까요?
using System;

namespace Ex6_2
{
	class MainApp
    {
    	public static void Main()
        {
        	double mean = 0;
            
            Mean(1, 2, 3, 4, 5, mean);
            
            Console.WriteLine("평균 : {0}", mean);
         }
         
         public static void Mean(
         	double a, double b, double c,
            double d, double e, double mean)
         {
         	mean = (a + b + c + d + e) / 5;
         }
     }
 }
  1. 다음 코드에 Plus() 메소드가 double형 매개변수를 지원하도록 오버로딩하세요. 이 프로그램이 완성된 후의 실행 결과가 다음과 같아야 합니다.
3 + 4 = 7
2.4 + 3.1 = 5.5
using System;

namespace Ex6_3
{
	class MainApp
    {
    	public static void Main()
        {
        	
            int a = 3;
            int b = 4;
            int resultA = 0;
            
            Plus(a, b, out resultA);
            
            Console.WriteLine("{0} + {1} = {2}", a, b, resultA);
            
            double x = 2.4;
            double y = 3.1;
            double resultB = 0;
            
            Plus(x, y, out resultB); // 오버로드가 필요한 메소드입니다.
            
            Console.WriteLine("{0} + {1} = {2}", x, y, resultB);
        }
        
        public static void Plus(int a, int b, out int c)
        {
        	c = a + b;
        }
        
        
    }
}
       

0개의 댓글