본문 바로가기

C#/이것이 C#이다

[ 이것이 C#이다 ] Chapter 06 . 메소드로 코드 간추리기

[ 학습 흐름 ]

[ 학습 흐름 ]

  1. 메소드 알아보기
  2. return
  3. 매개변수와 인자
  4. 참조에 의한 매개변수 전달
  5. 출력 전용 매개변수
  6. 메소드 오버로딩
  7. 가변 개수의 인수
  8. 명명된 인수
  9. 선택적 인수

[ 메소드란 ]

[ 메소드 ]

메소드

-  일련의 코드를 하나로 묶은 것 .  C/C++에서는 함수 / 파스칼에서는 프로시저

- 메소드가 함수 , 프로시저 , 서브루틴과 다른점은 클래스 내부에 존재한다는 점 .

class 클래스 이름
{
    [한정자] [반환현식] [메소드 이름] ( 매개변수 목록 )
    {
        //실행 코드
        //실행 코드
        return 메소드 결과;
    }
}

매개 변수와 반환 형식

-  매개변수는 제품을 만들기 위해 기계(메서드)에 넣는 재료

- 매서드의 반환형식은 제품의 규격

 


메소드의 호출 과정

- 호출자가 메소드를 호출하면서 인수를 넘긴다

- 인수는 메소드의 매개 변수에 입력

- 호출을 받은 메소드는 매개변수를 이용하여 계산 수행후 결과를 호출자에게 반환


매개변수와 인수

- 매개변수는 메소드가 호출자에게 전달받은 값을 받는 변수

- 인수는 호출자가 매개변수에 넘기는 값

using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;

namespace Method;

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
{
    //프로그램 실행이 시작되는 곳
    static void Main(string[] args)
    {
        int result = Calculator.Plus(3, 4); 
        Console.WriteLine(result);  

        result=Calculator.Minus(5, 2);
        Console.WriteLine(result);
    }
}

 


void 반환형식

- 모든 메소드들이 결과를 반환하는건 아님 ex) Console.WriteLine()

- 반환 형식에 void를 넣어주면 됨


static 한정자

- 메소드나 필드가 클래스의 인스턴스가 아닌 클래스 자체에 소속되도록 지정하는 한정자

- 해당 클래스의 인스턴스를 만들지 않고도 메소드를 호출 할 수 있다 .


[ return에 관하여 ]

[ return ]

메소드

-  return 문은 점프문의 한 종류로 프로그램의 흐름을 호출자에게 돌려놓는다 .

- 메소드 중간에 호출되어 메소드를 종결 , 프로그램의 흐름을 호출자에게 돌려준다 .

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 : {name} Phone : {phone}");
    }
    //프로그램 실행이 시작되는 곳
    static void Main(string[] args)
    {
        Console.WriteLine($"10번째 피보나치 수 : {Fibonacci(10)}");
        PrintProfile("", "123-4567");
        PrintProfile("박상현", "456-789");
    }
}

 


[ 매개변수에 대하여 ]

[ 매개변수의 전달 ]

매개 변수의 전달

int x =3;
int y=5;

int result = Calculator.Plus(x,y);

-  위 코드에서 x와 y는 실제로 Calculator.Plus 메서드에 들어가지 않는다 .

- 매개변수도 근본적으로 변수기에 메소드 외부로부터 메소드 내부로 데이터를 전달받는 매개체 역할을 할 뿐이다 .

- 즉 ,그 데이터가 참조든 값이든 내부 데이터가 복사될 뿐이다 .

- x와 Plus 메서드의 매개변수a는 같은 데이터(3)를 가지고 있지만 별개의 메모리 공간을 사용한다


값에 의한 전달

public static void Swap(int a,int b)
{
    int temp=b;
    b=a;
    a=temp;
}

- 메소드를 호출할 때 데이터를 복사해서 매개변수에 넘기는 것을 값에 의한 전달이라 한다 .


참조에 의한 전달

- 매개변수가 메소드에 넘겨진 원본 변수를 직접 참조한다 .

- 메소드 안에서 매개변수를 수정하면 원본 변수에 수정이 이루어진다 .

- ref 키워드를 매개변수 앞에 붙이면 참조에 의한 전달이 이루어진다 .

public static void Swap(ref int a,ref int b)
{
    int temp=b;
    b=a;
    a=temp;
}

//호출시도 ref 키워드를 매개변수 앞에 붙여준다
Swap(ref x,ref y);

 

using System;

namespace Return;

class MainApp
{
    static void Swap(ref int a,ref int b)
    {
        int temp = b;
        b = a;
        a = temp;
    }
    //프로그램 실행이 시작되는 곳
    static void Main(string[] args)
    {
        int a = 3;
        int b=4;
        Console.WriteLine($"a : {a} b : {b}");

        Swap(ref a,ref b);
        Console.WriteLine($"a : {a} b : {b}");
    }
}

 


[ 메소드의 결과를 참조로 반환하기 ]

[ 참조 반환값 ]

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

class SomeClass
{
	int SomeValue=10;
    
    //ref 키워드로 메소드를 한정한다
    public ref int SomeMethod()
    {
    	//return문을 사용할 때 ref 키워드를 반환할 필드/객체 앞에 붙여준다 .
    	return ref SomeValue;
    }
}

//호출자가 특별한 키워드를 사용하지 않는 이상 값을 반환하는 평범한 메소드처럼 동작
SomeClass obj = new SomeClass();
int result = obj.SomeMethod();

//반환 결과를 호출자가 참조로 넘겨받고 싶다면 ref 키워드 사용
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();
        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}");
    }
}

 


[ 출력 전용 매개변수 ]

[ 두개 이상의 결과를 요구하는 메서드 ]

- 나눗셈을 구현할 때 제구와 피제수를 매개변수로 넘겨 받고 결과는 몫과 나머지로 반환해야 한다 . 

- 위와 같이 두개 이상의 결과를 요구하는 메서드가 있다면 어떻게 구현해야 할까?


[ ref 키워드를 이용 ]

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

- ref 키워드를 이용, 여러가지 결과를 메서드에서 얻어올 수 있다 .

- 다만 , 안전장치가 없다 . ex ) 메소드가 해당 매개변수에 결과를 저장하지 않아도 컴파일러는 경고하지 않는다 .


[ out 키워드를 이용 ]

사용법

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

- out 키워드를 이용한 "출력 전용 매개변수"는 안전한 방법으로 위와 같은 일을 하게 해준다 .


out 키워드의 장점

- 메소드가 해당 매개변수에 값을 저장하지 않으면 컴파일러가 에러 메시지를 출력한다 .

- 호출자는 초기화 하지 않은 지역변수를 메소드의 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;
        Divide(a, b, out int c, out int d);
        Console.WriteLine($"a : {a} b : {b} a/b : {c} a%b : {d}");
    }
}

 


[ 메소드 오버로딩 ]

[ 메소드 오버로딩 ]

- 하나의 메소드 이름에 여러개의 구현을 올리는 것

-  컴파일러는 메소드 호출시 반환형식은 묻지도 않고 매개변수의 수와 형식만 분석하여 어느 버전이 호출될지를 찾아준다

- 컴파일 타임에 이루어지기에 성능저하는 걱정하지 않아도 됨

- 이름에 대한 고민을 줄여주고 , 코드를 일관성 있게 유지한다 .

static int Plus(int a,int b)
{
	return a+b;
}

static int Plus(int a,int b,int c)
{
	return a+b+c;
}

static double Plus(double a,double b)
{
	return a+b;
}

static double Plus(double a,double b,double c)
{
	return a+b+c;
}

[ 가변 개수의 인수 ]

[ 가변개수의 인수 ]

- 인수의 개수가 다르다는 이유만으로 같은 메서드를 여러 버전으로 오버로딩 하고 싶을 수 있다 .

- 가변개수의 인수는 그 개수가 유연하게 변할 수 있는 인수를 말한다 .

- params 키워드와 배열을 이용하여 선언한다 .

- 메소드 오버로딩은 매개변수의 형식/개수가 다를때 사용한다

- 반면 , 인수의 형식은 같으나 개수가 유연해야 한다면 가변개수의 인수를 사용한다 .

using System;

namespace UsingParams;


class MainApp
{   
    static int Sum(params int[] args)
    {
        Console.WriteLine("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: {sum}");
    }
}

 


[ 명명된 인수 ]

[ 명명된 인수 ]

- 메소드 호출시 매개변수 목록 중 어느 매개변수에 데이터를 할당할지 지정하는 것은 순서이다 .

- 명명된 인수는 인수의 이름에 근거해서 데이터를 할당 할 수 있다 .

- 메소드 호출시 인수의 이름뒤에 콜론을 붙여 할당할 데이터를 넣어주면 된다 .

- 이는 코드의 가독성을 높여준다 .

static void PrintProfile(strin name,string phone)
{
	Console.WriteLine($"Name : {name} Phone : {phone}");
}

static void Main(string[] args)
{
	PrintProfile(name:"Yeo",phone:"10122");
}

[ 선택적 인수 ]

[ 선택적 인수 ]

- 메소드의 매개변수는 기본값을 가질 수 있다 .

- 기본값을 가진 매개변수는 메소드 호출시 해당 인수를 생략 할 수 있다. 

- 선택적 인수는 항상 필수 인수의 뒤에 와야 한다 .

- 모호함의 문제는 명명된 인수와 함께 사용해 해결 할 수 있다 . 

static void PrintProfile(strin name,string phone="")
{
	Console.WriteLine($"Name : {name} Phone : {phone}");
}

static void Main(string[] args)
{
	PrintProfile(name:"Yeo",phone:"10122");
	PrintProfile(name:"KIM");
    
}

[ 메소드 오버로딩 VS 선택적 매개변수 ]

void MyMethod(string arg0="",string arg1="")
{
	Console.WriteLine(A);
}

void MyMethod()
{
	Console.WriteLine(B);
}

- 위와 같이 메소드 오버로딩과 선택적 매개변수가 같이 사용되면 0점자리 코드이다 .

- 논리는 동일하되 매개변수가 다른경우는 선택적 인수를 사용하자

- 매개변수에 따라 논리도 바뀐다면 오버로딩을 사용하자


[ 로컬 함수 ]

[ 로컬 함수 ]

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

- 클래스의 멤버가 아니기에 메소드가 아닌 함수라 불린다 .

- 로컬함수는 자신이 속한 메소드의 지역변수를 사용 할 수 있다 .

- 메소드 밖에서는 쓸 일 없는 반복적인 작업을 하나의 이름아래 묶는데 제격이다 .(람다식과 더불어 코드를 간추릴 옵션)

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)
        {
            //ASCII : A _Z 는 65 ~ 90 / a -z 는 97 ~ 122
            if (arr[i] < 65 || arr[i] > 90)
                return arr[i];
            else
                return (char)(arr[i]+32);
        }
        return new string(arr);
    }
    static void Main(string[] args)
    {
        Console.WriteLine(ToLowerString("HELLO"));
        Console.WriteLine(ToLowerString("GOOD MORNING"));
    }
}