메소드 – 이것이 C# 이다.
– 메소드
– 선언 형식
– 매개변수 / 반환 형식
– return
– 매개변수에 대하여
– 참조에 의한 매개변수 전달
– 결과를 참조로 반환(참조 반환값)
– 출력 전용 매개변수
– 메소드 오버로딩
– 가변 개수의 인수
– 명명된 인수
– 선택적 인수
– 로컬 함수
– 메소드 –
메소드 ( Method ) 는 객체지향 프로그래밍 언어에서 사용하는 용어이며 일련의 코드를 하나의 이름 아래 묶은 것이라고 할 수 있다.
묶인 코드는 메소드의 이름을 불러서 사용하는 데 이것을 “메소드를 호출한다” 라고 한다.
– 선언 형식 –
Class 클래스 이름 { 한정자 반환형식 메소드 이름 (매개변수 목록) { // 실행하고자 하는 코드1 // 실행하고자 하는 코드2 // ... // 실행하고자 하는 코드 n reutrun 메소드 결과; } }
– 매개변수 / 반환 형식 –
메소드의 선언이 다른 언어들과 다른점은
C# 은 객체 지향 프로그래밍 언어이고, 객체 지향 프로그래밍에서는 코드 내의 모든 것을 객체로 표현합니다.
각 객체는 자신만의 속성(데이터) 기능(메소드)를 가지고 있는데. 클래스가 바로 이 객체를 위한 청사진을 제공합니다.
메소드 ( Method ) 는 방법, 방식이라는 뜻을 갖고 있지만 C#에서는 객체의 일을 처리하는 방법 또는 방식으로 이해할 수 있습니다.
클래스 안에 선언되는 메소드는 매개변수(Parameter) 와 반환 형식(Return Type)을 가집니다.
매개변수(Parameter) 는 제품을 만들기 위해 기계(메소드)에 집어넣는 재료라고 할 수 있습니다.
※ 인수는 호출자가 매개변수에 넘기는 값을 뜻합니다.
반환 형식(Return Type) 은 이 제품의 규격이라고 할 수 있습니다.
※ 메소드들이 모두 결과를 반환하는 것은 아닙니다.
반환 형식이 없는 메소드를 선언할 때에는 “void” 를 반환 형식으로 넣어주면 됩니다.
// 메소드 예시 // 클래스 class Calculator { // 한정자, 반환 형식, 이름 public static int Plus(int a, int b) { Console.WriteLine("Input : {0}, {1}", a, b); int result = a + b; // 반환 값 return result; } }
추가 예제
using System; namespace ConsoleApplication1 { class Calculator { public static int Plus(int a, int b) { return a + b; } public static int Minus(int a, int b) { return a - b; } } class MainApp { public static void Main() { int result = Calculator.Plus(3, 4); Console.WriteLine(result); result = Calculator.Minus(5, 2); Console.WriteLine(result); } } }
– return –
return 문은 점프문의 한 종류입니다.
프로그램의 흐름을 호출자에게 돌려놓는 기능을 합니다.
위치에 상관없이 메소드 호출되어 메소드를 동결시키고 프로그램의 흐름을 호출자에게 돌려줍니다.
메소드가 자기 자신을 스스로 호출하는 것을 재귀 호출(Recursive Call) 이라고 합니다.
다음 예제는 return 과 이를 이용한 Fibonacci 수열 코드입니다.
using System; namespace Return { class MainApp { 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:{0}, Phone:{1}", name, phone); } static void Main(string[] args) { Console.WriteLine("10번째 피보나치 수 : {0}", Fibonacci(10)); PrintProfile("", "981-4575"); PrintProfile("Lycos", "768-1320"); } } }
– 매개변수에 대하여 –
메소드에서 매개변수로 넘겨지는 인수는 실제로 메소드 안으로 들어가는 것인가? X
메소드 외부로부터 메소드 내부로 데이터를 전달받는 매개체 역할을 할 뿐입니다.
매개변수도 근본적으로 변수 입니다.
한 변수를 또 다른 변수에 할당하면 변수가 담고 있는 데이터만 복사될 뿐입니다.
인수와 매개변수는 같은 데이터를 가지고 있지만 별개의 공간을 사용합니다.
이처럼 메소드를 호출할 때 데이터를 복사해서 매개변수에 넘기는 것을 ” 값에 의한 전달(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() 메소드는 두 매개변수의 값을 교환할 수 없었습니다.
위와 같은 방법으로 값을 교환하려면 ref 키워드를 이용하여 “ 참조에 의한 전달(pass by reference) “로 넘기면 가능합니다.
using System; namespace SwapByValue { class MainApp { public static void Swap(ref int a,ref 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(ref x,ref y); Console.WriteLine("x:{0}, y:{1}", x, y); } } }
Swap() 메소드가 참조 매개변수를 전달하는 과정
– 결과를 참조로 반환(참조 반환값) –
바로 위의 내용은 매개변수를 메소드에 참조로 전달하는 방법에 대한 것 입니다.
다음 방법은 메소드의 결과를 참조로 반환하는 참조 반환값(ref return)입니다.
참조 반환값을 이용하면 메소드의 사용자로 하여금 반환받은 결과를 참조로 다룰 수 있도록 합니다.
ref 한정자를 이용하여 메소드를 선언 / return 문이 반환하는 변수 앞에도 ref 키워드를 명시해야합니다.
또한 메소드가 반환하는 결과를 호출자가 참조로 넘겨 받으려면
결과를 담는 지역변수( = 참조 지역변수(ref local))와 호출할 메소드의 이름 앞에 ref 키워드를 위치해야 합니다.
using System; namespace RefReturn { class Product { private int price = 100; public ref int GetPrice() // ref { return ref price; // ref } public void PrintPrice() { Console.WriteLine($"Price :{price}"); } } class MainApp { static void Main(string[] args) { Product carrot = new Product(); // 참조 지역변수, 메소드 // 수정하면 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($"Local Price :{ref_local_price}"); Console.WriteLine($"Normal Local Price :{normal_local_price}"); } } }
– 출력 전용 매개변수 –
대개의 경우 메소드의 결과는 하나면 충분합니다.
하지만 두개 이상의 결과를 요구하는 메소드도 있습니다. ex) 나머지 10 % 3 = 몫 : 3 / 나머지 : 1
ref 키워드를 사용하여 구현해도 좋지만 C# 에서는 더 안전한 방법으로 같은 일을 할 수 있습니다.
바로 out 키워드를 이용한 출력 전용 매개변수 입니다.
ref 키워드 대신 out 키워드를 사용하는 것 입니다.
그렇다면 두 키워드의 차이는 무엇일까요?
ref 키워드를 이용하여 매개변수를 넘기는 경우에는 메소드가 해당 매개변수 결과를 저장하지 않아도 컴파일러는 아무런 경고를 하지 않습니다.
하지만 out 키워드를 이용하여 매개변수를 넘기는 경우
메소드가 해당 매개변수에 결과를 저장하지 않으면 컴파일러가 에러메시지를 출력합니다.
또한 메소드를 호출하는 쪽에서는 초기화하지 않은 지역변수를 메소드의 out 매개변수로 넘길 수 있습니다.
(컴파일러가 호출당하는 메소드에서 그 지역 변수를 할당할 것을 보장함)
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; // 미리 선언할 필요 없음 // int c; // int d; 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); } } }
– 메소드 오버로딩 –
오버로딩(overloading)이란? 과적하다 라는 뜻 입니다.
메소드의 오버로딩은 하나의 메소드 이름에 여러 개의 구현을 올리는 것을 뜻합니다.
같은 메소드의 이름에 호출에 사용되는 매개변수의 수/ 매개변수의 형식을 이용하여 구현합니다.
( 반환 형식은 X )
메소드 오버로딩은 이름에 대한 고민을 줄여주는 동시에 코드를 일관성 있게 유지해 줍니다.
코드의 일관성은 메소드의 작성자 및 사용자에게 도움과 생산성을 제공합니다. ex) WriteLine() 메소드는 18개의 버전을 오버로딩함
아래는 Plus 메소드의 오버라이딩 예제입니다.
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)); } } }
– 가변 개수의 인수 –
프로그래밍을 하다보면 그저 인수의 개수가 다르다는 이유만으로 똑같은 메소드를 오버로딩하고 싶을 때가 있습니다.
이러한 경우에 가변 개수의 인수라는 기능을 사용하여 쉽게 구현할 수 있습니다.
가변 개수의 인수란, 그 개수가 유연하게 변할 수 있는 인수를 말합니다.
메소드 오버로딩은 매개변수의 개수뿐만 아니라 형식이 다른 경우에 사용하므로
매개변수의 개수가 유한하게 정해져 있으면 메소드 오버로딩을 사용하는것이 적합합니다.
가변 개수의 인수는 형식은 같으나 인수의 개수만 유연하게 달라질 수 있는 경우에 적합합니다.
가변 개수의 인수는 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); } } }
– 명명된 인수 –
메소드를 호출할 때 매개변수 목록중 어느 매개변수에 데이터를 할당할지 지정하는 것은 목록의 순서입니다.
명명된 인수(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"); } } }
– 선택적 인수 –
메소드의 선언시에 매개변수를 특정값으로 초기화하듯이 선언 할 수 있습니다.
매개변수가 기본값을 가지며 필요한 경우에 할당이 가능하는 것을 선택적 인수(Optional Argument)라고 합니다.
선택적 인수는 편의를 제공하지만 또 한편으로는 모호함을 줍니다.
매개변수의 수가 많고 선택적 인수도 여럿 포함되면 코드를 작성하는데 어려움이 생길 수 있습니다.
따라서 선택적 인수와 앞서 배운 명명된 인수를 같이 사용하는 것을 추천합니다.
또한 프로그래머는 메소드 오버로딩과 선택적 인수 둘 사이의 모호성으로 인해서 현재 상황에 어느 방법이 좋을지 결정해야합니다.
논리는 동일하고 매개 변수가 다른 경우 선택적 인수 / 매개 변수에 따라서 논리도 함께 달라지는 경우는 오버로딩을 사용하는 것이 좋습니다.
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"); } } }
– 로컬 함수 –
로컬 함수(Local Function)는 메소드 안에서 선언되고. 선언된 메소드 안에서만 사용되는 특별한 함수입니다.
클래스의 맴버가 아니라서 메소드가 아닌 함수(Function)라고 표현됩니다.
로컬 함수의 특징은 자신이 존재하는 지역에 선언되어 있는 변수를 사용할 수 있다는 것 입니다.
선언 방법은 메소드와 크게 다르지 않습니다.
using System; namespace LocalFunction { class MainApp { static string ToLowerString(string input) { char arr = input.ToCharArray(); for(int i=0; i<arr.Length; i++) { arr[i] = ToLowerChar(i); } char ToLowerChar(int i) // 로컬 함수 선언 { // 지역 변수 arr 사용 if (arr[i] < 65 || arr[i] > 90) // A~Z의 ASCII 값 : 65 : 90 return arr[i]; else // a~z의 ASCII 값 : 97 : 122 return (char)(arr[i] + 32); } return new string(arr); } static void Main(string[] args) { Console.WriteLine(ToLowerString("Hello!")); Console.WriteLine(ToLowerString("Good Morning.")); Console.WriteLine(ToLowerString("This is C#.")); } } }