이번 포스팅에서는 C#의 함수(Function) 및 메서드(Method)의 정의와 사용법에 대해 알아보겠습니다.
특히 이를 사용할 때 같이 사용되는 개념들과 사용법에 대해 알아보고 C#의 최신 문법에는 어떻게 사용하는 경향들이 있는지도 알아보도록 하겠습니다.
함수(Function) vs 메서드(Method)
개요
기본적으로 함수(Function)와 메서드(Method)는 코드의 재사용성을 높이고 프로그램의 구조를 체계적으로 관리할 수 있다는 공통점이 있습니다.
허나, 함수(Function)와 메서드(Method)는 매우 유사해 보이지만, 그 사용범위와 정의하는 컨텍스트에서 미묘한 차이가 있습니다.
이 두 용어는 종종 서로 바꿔 사용되지만, 프로그래밍 언어와 패러다임에 따라 의미하는 바가 조금씩 다를 수 있습니다.
함수 (Function)
함수는 일반적으로 다음과 같은 특징을 가집니다:
- 독립적인 역할:
- 함수는 프로그램 내에서 독립적으로 존재할 수 있습니다.
- 즉, 객체나 클래스에 속하지 않고도 정의하고 호출할 수 있습니다.
- 입력과 출력:
- 함수는 일반적으로 입력(인자)를 받아 처리하고 결과(반환값)를 출력합니다.
- 재사용성:
- 함수는 코드의 재사용을 증진시키는 주요 도구로, 특정 작업을 수행하는 코드 블록을 반복적으로 사용할 수 있도록 합니다.
메서드 (Method)
메서드는 함수와 유사한 특징을 가지지만, 주로 객체 지향 프로그래밍에서 사용되는 용어로, 다음과 같은 특성을 가집니다:
- 클래스 또는 객체에 속함:
- 메서드는 특정 클래스 또는 객체의 일부로 정의됩니다.
- 메서드는 클래스의 인스턴스 데이터(속성)에 접근하고 조작하는 데 사용됩니다.
- 캡슐화와 상호작용:
- 메서드는 객체의 캡슐화된 데이터를 조작하는 주된 수단으로, 객체 내부의 상태를 변경하거나 객체 간의 상호작용을 담당합니다.
- 상속과 다형성:
- 객체 지향 언어에서 메서드는 상속을 통해 확장되거나 재정의될 수 있으며, 다형성의 주요 요소로 작용합니다.
간단히 말하면, 함수는 독립적으로 존재 가능한 기능의 단위 이고, 메소드는 클래스에 포함되어 있는 함수를 메소드 라고 부릅니다.
허나 현업에서 이를 명확하게 구분하여 부르지는 않습니다.
C#(Csharp)에서 메서드(Method) 기초
메서드(Method) 기본 구조
C#에서 메서드를 정의하는 기본 구조는 다음과 같습니다.
[접근제어자] [반환형] 메서드이름([매개변수]) { // 메서드 본문 }
- 접근 제어자(Access Modifier):
- 메서드에 대한 접근을 제어합니다.
public
,private
,protected
,internal
등이 있습니다. - 상세 설명은 앞선 글([C# – 기초 강좌] 3. Csharp 기초 문법 이해하기 (Variable, Operator, Loop))을 참조해주세요
- 메서드에 대한 접근을 제어합니다.
- 반환형(Return Type):
- 메서드가 수행 후 반환하는 데이터의 타입입니다. (반환값이 없을 경우
void
를 사용합니다.)
- 메서드가 수행 후 반환하는 데이터의 타입입니다. (반환값이 없을 경우
- 메서드 이름(Method Name):
- 메서드를 호출할 때 사용하는 이름입니다.
- 매개변수(Parameters):
- 외부에서 메서드로 전달되는 값을 받기 위한 변수들입니다.
- 필요에 따라 매개변수가 없을 수도 있습니다.
정의 및 호출
아래 예제에서 Calculator
클래스 내에 Add()
메서드를 정의했습니다.
Add()
메서드는 두 개의 정수 매개변수(a
와 b
)를 받아 그 합을 반환합니다.
메인 프로그램에서는 Calculator
클래스의 인스턴스를 생성하고, Add()
메서드를 호출하여 그 결과를 출력합니다
public class Calculator { // Define Add() method with two parameters public int Add(int a, int b) { return a + b; } } public class Program { public static void Main(string[] args) { Calculator calc = new Calculator(); // call Add() method int result = calc.Add(5, 3); Console.WriteLine("5 + 3 = " + result); } }
매개변수(Parameters)와 반환값(Return Value)
매개변수 (Parameters)
매개변수는 메서드에 전달되는 입력 값입니다.
메서드를 정의할 때, 매개변수를 통해 메서드가 처리할 데이터의 유형과 개수를 지정할 수 있습니다.
매개변수는 메서드 내부에서 변수처럼 사용되며, 메서드 호출 시 지정된 값으로 초기화됩니다.
위 예제에서는 Add()
메서드의 int a, int b
가 매개변수에 해당합니다.
매개변수는 다음과 같이 기본 값(Default Value)를 지정하여 해당 매개변수의 값의 입력을 생략할 수도 있습니다.
public class Calculator { // Define Add() method with two parameters (b has a default value 10) public int Add(int a, int b = 10) { return a + b; } }
반환값 (Return Value)
반환값은 메서드가 수행한 계산이나 작업의 결과를 호출한 곳으로 보내는 값입니다.
메서드를 정의할 때 반환 타입을 지정해야 하며, 반환 타입이 void
가 아닌 경우 메서드 내에서 반드시 return
문을 사용해 해당 타입의 값을 반환해야 합니다.
위 Add()
메서드에서는 integer 타입을 반환하도록 정의하였습니다.
오버로딩(Overloading)과 재귀 함수(Recursive Function)
개요
오버로딩(Overloading) 은 같은 이름의 메서드를 매개변수의 유형이나 개수가 다르게 하여 여러 버전을 만드는 기법입니다.
재귀 함수(Recursion) 는 메서드가 자기 자신을 호출하는 방식으로 작동합니다.
오버로딩(Overloading)
메서드 오버로딩은 같은 이름의 메서드를 다른 매개변수 세트로 여러 번 정의하는 것을 말합니다.
이를 통해 같은 연산에 대해 다양한 입력 유형이나 매개변수의 수를 처리할 수 있습니다.
예제
다음과 같이 매개변수와 반환형으로 int 형과 double 형을 다르게 받는 두 함수 Multiply()
를 정의합니다.
public class Calculator { // Overloading: Multiply() method with different parameters public int Multiply(int x, int y) { Console.WriteLine("Multiply(int x, int y)"); return x * y; } public double Multiply(double x, double y) { Console.WriteLine("Multiply(double x, double y)"); return x * y; } } public class Program { public static void Main(string[] args) { // call Multiply() method (overloading) int result1 = calc.Multiply(5, 3); Console.WriteLine("5 * 3 = " + result1); double result2 = calc.Multiply(5.5, 3.5); Console.WriteLine("5.5 * 3.5 = " + result2); } }
다음과 같이 int 타입을 매개변수로 넣었을 때와 double 타입을 매개변수로 넣었을 때,
다른 함수가 호출되었음을 알 수 있습니다.
Multiply(int x, int y) 5 * 3 = 15 Multiply(double x, double y) 5.5 * 3.5 = 19.25
재귀 함수
재귀 함수는 자기 자신을 호출하는 함수로, 복잡한 문제를 간단하게 분해하여 해결할 수 있습니다.
대표적인 예로 팩토리얼 계산이 있습니다.
예제
다음 예제에서 Factorial()
메서드는 종료 조건을 만날 때까지 반복적으로 재귀함수인 Factorial()
를 호출합니다.
public class Calculator { // recursive method example public int Factorial(int n) { if (n <= 1) return 1; // finish condition return n * Factorial(n - 1); // call recursive method again } } public class Program { public static void Main(string[] args) { // call Factorial() method int n = 5; int resultFactorial = calc.Factorial(n); Console.WriteLine("Factorial of " + n + " is " + resultFactorial); } }
익명 메서드 (Anonymous method)
이름이 없는 메서드로, 주로 대리자(delegate)를 사용하여 이벤트 핸들러나 다른 메서드 호출에서 코드를 인라인으로 삽입할 때 사용됩니다.
익명 메서드는 람다 표현식이 도입되기 전에 자주 사용되었고, 여전히 레거시 코드에서 볼 수 있습니다
Func<int, int, int> add = delegate (int x, int y) { return x + y; }; // Define an anonymous method Console.WriteLine(add(3, 4)); // Use the anonymous method
C#에서의 최신 함수 및 메서드 사용법
1. 로컬 함수 (Local Functions)
로컬 함수는 메서드 내부에 선언되는 함수로, 외부에서는 접근할 수 없어 캡슐화를 강화할 수 있습니다.
예제
int AddLocal(int x, int y) => x + y; // Define a local function Console.WriteLine(AddLocal(3, 4)); // Call the local function
2. 람다 표현식(Lambda Expressions)
익명 함수를 생성하는 간결한 방법으로, 주로 LINQ 쿼리, 이벤트 처리, Func
와 Action
과 같은 대리자(delegate) 사용에 활용됩니다.
람다 표현식을 이해하고 사용하는 것은 코드를 더 간결하고 가독성 있게 만드는 데 큰 도움이 됩니다.
람다 표현식의 기본 구조는 다음과 같습니다.
(입력 매개변수) => 표현식 또는 { 문장; }
예제
Func<int, int, int> multiply = (x, y) => x * y; // Define a lambda expression Console.WriteLine(multiply(5, 2)); // Use the lambda expression
3. 표현식 본문 정의(Expression-bodied member)
표현식 본문 정의(Expression-bodied member)는 C# 6.0에서 도입된 문법으로, 클래스 멤버들을 더욱 간결하게 표현할 수 있게 해주는 기능입니다.
이 문법은 주로 간단한 메서드나 속성에 대한 정의를 한 줄의 코드로 축약할 수 있게 해줍니다.
이는 코드의 가독성을 향상시키고, 메서드와 속성의 선언을 더욱 간결하게 만들어줍니다.
예제
public class Person { private string name; // Constructor with Expression-bodied member public Person(string name) => this.name = name; // Constructor // Property with Expression-bodied member public string Name { get => name; // Property set => name = value; } public void Print() => Console.WriteLine(name); // Method with Expression-bodied member }
4. 비동기 메서드 (Async Method)
비동기 메서드(async
메서드)는 비동기 프로그래밍을 위해 특별히 설계된 기능으로, async
와 await
키워드를 사용하여 구현됩니다.
이러한 메서드들은 주로 I/O 작업, 긴 연산, 웹 서비스 호출 등과 같이 시간이 많이 소요되는 작업을 처리할 때 사용되며, 이를 통해 UI 응답성을 유지하거나 서버의 스레드 사용을 최적화할 수 있습니다.
반환 타입으로 Task<T>
또는 void
타입으로 반환합니다.
예제
async
키워드를 넣어 비동기 함수를 정의하고, 비동기 함수를 await
을 사용하여 호출하고 작업완료를 기다리게 됩니다.
await
키워드는 비동기 함수 내에서만 사용할 수 있습니다.
public class Program { public static async Task<string> DownloadDataAsync(string url) { using (HttpClient client = new HttpClient()) { HttpResponseMessage response = await client.GetAsync(url); if (response.IsSuccessStatusCode) { string content = await response.Content.ReadAsStringAsync(); return content; } else { throw new Exception("Failed to download data."); } } } public static async Task Main(string[] args) { try { string url = "https://www.google.com"; string resDownload = await DownloadDataAsync(url); Console.WriteLine("Downloaded data:"); Console.WriteLine(resDownload); } catch (Exception ex) { Console.WriteLine("An error occurred: " + ex.Message); } } }
참고 링크
- My Git Repository (devitworld-csharp-basic) – DevitworldConsoleApp/4_FunctionMethod