-
C# 기초지식, 정보 정리 (면접 질문 대비, 꼭 알아야 하는 것)닷넷/C# 2023. 4. 11. 18:38반응형
미루고 미루었던, ChatGPT를 활용하면 좀 더 수월하게 작성할 수 있게 되었으므로, C#에 대해서 꼭 알아야 할 기초 지식과 정보를 정리하였습니다.
(링크드인 C# 테스트를 오늘 해 봤는데 영어 독해를 잘 못 했다고 해도, 그 용어들에 대해 알았다면 쉬운 문제인데 정답을 맞추지 못한 문제들이 많아서 나의 부족을 깨닫고 다시 공부하고 정리할 겸 쓰는 건 안 비밀)
1. C#이란
C#(C-Sharp)은 Microsoft에서 개발한 객체 지향 프로그래밍 언어입니다. Anders Hejlsberg가 설계한 이 언어는 C++와 Java와 유사한 구문을 가지고 있으며, 현대적인 프로그래밍 언어의 기능을 제공합니다.
C#은 .NET Framework와 함께 사용되어 다양한 종류의 Windows 응용 프로그램 개발에 적합하며, 크로스 플랫폼 개발을 지원하는 .NET Core에서도 사용됩니다. 현재는 닷넷프레임워크와 닷넷코어를 통합하고 좀 더 발전된 형태인 닷넷5부터 닷넷6, 닷넷7이 있습니다.C#은 다음과 같은 종류의 프로그램 개발에 사용할 수 있습니다
- 윈도우 폼 애플리케이션 (WinForms, WPF)
- 웹 애플리케이션 (ASP.NET)
- 모바일 애플리케이션 (Xamarin)
- 게임 개발 (Unity)
- 콘솔 애플리케이션
- 웹 서비스 및 마이크로서비스
- 클라우드 기반 애플리케이션
C#은 간결하고 표현력이 풍부한 문법을 제공하며, 강력한 타입 시스템, 가비지 컬렉션, 제네릭, LINQ(Language Integrated Query), 비동기 프로그래밍 등과 같은 다양한 기능을 지원합니다.
C#은 지속적으로 업데이트되고 발전하며, 현재는 C# 11 버전이 최신입니다. (2023년 4월 기준)이를 통해 최신 기술 및 도구를 사용하여 프로젝트를 진행할 수 있습니다.
한국어 책이 나와 있는 기준으로는 C# 10을 최신으로 볼 수 있습니다. 이에 대한 자세한 자료는 C# 전문가 정성태님이 작성하신 책을 보며 공부합니다.
2. 변수와 자료형 (Variables and Data Types)
C#의 주요 자료형에는 정수형, 실수형, 문자형, 논리형, 객체형 등이 있습니다. 변수는 데이터를 저장하는 기본 단위로, 선언 시에는 변수의 자료형과 이름이 필요합니다. C#의 자료형은 값 형식 (Value Type)과 참조 형식(Reference Type)으로 구분됩니다.
1) 값 형식(Value Type)
메모리 상에 데이터 값 자체가 저장되는 형식으로, 기본 자료형이 여기에 속합니다.
- 정수형: byte(1바이트), short(2바이트), ushort(2바이트), int(4바이트), uint(4바이트), long(8바이트), ulong(8바이트) 등이 있습니다.
- 실수형: float(4바이트)와 double(8바이트)가 있습니다.
- 논리형: bool(참 또는 거짓 값을 나타냅니다. true 또는 false)
- 문자형: char(2바이트, 유니코드 문자를 저장합니다)
- 구조체(Struct): 사용자가 정의한 값 형식입니다.
2) 참조 형식(Reference Type)
메모리 상에 데이터 값이 아닌, 값이 저장된 메모리의 주소를 저장하는 형식입니다.
- 객체형: 클래스, 인터페이스, 델리게이트, 배열 등이 여기에 속합니다.
- 문자열: string형으로, 불변성을 가지며 참조 형식입니다.
- nullable 형식: 값 형식에 null 값을 할당할 수 있게 하는 형식입니다. 예를 들어 int? 와 같이 사용합니다.
변수 선언 방법은 다음과 같습니다.
자료형 변수이름;
예를 들어, 정수형 변수를 선언하려면 다음과 같이 작성합니다.
int myNumber;
변수를 선언함과 동시에 초기화하려면 다음과 같이 작성합니다.
int myNumber = 10;
자료형에 맞게 값을 할당하면 해당 변수를 사용하여 프로그램에서 다양한 연산 및 처리를 수행할 수 있습니다.
값 형식은 무엇이고, 참조 형식은 무엇인지를 아는 것은 중요합니다. 이때, 각각 메모리 상에 어디 영역에 할당되는지를 알면 더욱 좋습니다.
3. 메모리 영역
1) 값 형식(Value Type)
값 형식의 변수는 스택(stack) 영역에 할당됩니다. 값 형식에는 정수형, 실수형, 논리형, 문자형 및 사용자 정의 구조체 등이 포함됩니다. 스택은 선입후출(LIFO, Last-In-First-Out) 방식으로 데이터를 저장하며, 메서드의 호출이 종료되면 해당 메서드의 스택 프레임이 제거되어 메모리가 자동으로 해제됩니다.
2) 참조 형식(Reference Type)
참조 형식의 변수는 힙(heap) 영역에 할당됩니다. 참조 형식에는 클래스, 배열, 인터페이스, 델리게이트, 그리고 문자열(string) 등이 포함됩니다. 힙 영역에 할당된 객체들은 메모리 관리를 위해 가비지 컬렉터(Garbage Collector)가 관리합니다. 참조 형식의 변수는 힙에 있는 객체에 대한 메모리 주소를 스택에 저장하며, 실제 객체의 데이터는 힙에 위치합니다.
요약하면, 값 형식의 변수는 스택 영역에 할당되고, 참조 형식의 변수는 힙 영역에 할당됩니다. 참조 형식의 경우 스택에는 메모리 주소가 저장되고, 힙에는 실제 데이터가 저장됩니다.
4. 조건문과 반복문
C#에서는 if, else, switch, case 등의 조건문과 for, while, do-while 등의 반복문을 사용할 수 있습니다.
C#에서 조건문과 반복문은 프로그램의 흐름을 제어하는 데 사용되는 기본 제어 구조입니다. 조건문은 주어진 조건에 따라 코드의 실행 여부를 결정하고, 반복문은 특정 조건이 만족되는 동안 코드를 반복하여 실행합니다.
1) 조건문
(1) if문
if문은 주어진 조건이 참일 경우에만 실행되는 코드 블록입니다.
if (조건) { // 조건이 참일 때 실행할 코드 }
(2) if-else문
if-else문은 주어진 조건이 참일 경우와 거짓일 경우에 각각 다른 코드 블록을 실행합니다.
if (조건) { // 조건이 참일 때 실행할 코드 } else { // 조건이 거짓일 때 실행할 코드 }
(3) switch-case문
switch-case문은 주어진 변수의 값에 따라 실행할 코드 블록을 결정합니다.
switch (변수) { case 값1: // 변수 값이 값1일 때 실행할 코드 break; case 값2: // 변수 값이 값2일 때 실행할 코드 break; // ... default: // 변수 값이 어떤 case에도 해당하지 않을 때 실행할 코드 break; }
2) 반복문
(1) for문
for문은 초기화, 조건 검사, 증감식을 포함한 구문으로, 조건이 참인 동안 코드 블록을 반복 실행합니다.
for (초기화; 조건; 증감식) { // 조건이 참인 동안 실행할 코드 }
(2) while문
while문은 주어진 조건이 참인 동안 코드 블록을 반복 실행합니다.
while (조건) { // 조건이 참인 동안 실행할 코드 }
(3) do-while문
do-while문은 코드 블록을 실행한 후, 조건을 검사하여 조건이 참인 동안 계속 반복 실행합니다. 최소한 한 번은 코드 블록이 실행됩니다.
do { // 조건이 참인 동안 실행할 코드 } while (조건);
5. 배열과 컬렉션
배열은 고정 크기의 연속된 데이터 요소를 저장하는데 사용되며, 컬렉션은 동적 크기의 데이터 구조를 제공합니다.
C#에서 배열과 컬렉션은 데이터를 그룹화하여 저장하는 데 사용되는 자료구조이며 도구입니다. 배열은 고정된 크기의 연속적인 데이터를 저장하는 반면, 컬렉션은 가변 크기의 데이터를 저장하여 데이터를 유연하게 관리할 수 있고, 다양한 유형의 컬렉션 클래스가 제공됩니다.
사용자의 요구에 따라 적합한 자료구조를 선택하여 사용합니다.
1) 배열
배열은 동일한 데이터 타입의 원소들을 순차적으로 저장하는 고정 크기의 자료구조입니다. 배열은 차원에 따라 1차원, 2차원, 다차원 배열로 구분됩니다. 배열을 선언하고 초기화하는 방법은 다음과 같습니다.
// 1차원 배열 선언 타입[] 배열이름 = new 타입[길이]; // 예시: int형 1차원 배열 int[] numbers = new int[5]; // 배열 초기화 int[] numbers = new int[] {1, 2, 3, 4, 5}; // 또는 축약형 int[] numbers = {1, 2, 3, 4, 5};
2) 컬렉션
컬렉션은 다양한 유형의 가변 크기의 데이터를 저장하는 자료구조입니다. 컬렉션에는 여러 종류가 있으며, System.Collections 및 System.Collections.Generic 네임스페이스에서 찾을 수 있습니다.
(1) List
순차적인 데이터를 저장하는 동적 배열입니다.
using System.Collections.Generic; List<타입> 리스트이름 = new List<타입>(); // 예시 List<int> numbers = new List<int> {1, 2, 3, 4, 5};
(2) Dictionary
키-값 쌍을 저장하는 데이터 구조입니다. 키는 고유해야 합니다.
using System.Collections.Generic; Dictionary<키_타입, 값_타입> 딕셔너리이름 = new Dictionary<키_타입, 값_타입>(); // 예시 Dictionary<string, int> ages = new Dictionary<string, int> { {"Alice", 30}, {"Bob", 28} };
(3) HashSet
중복을 허용하지 않는 고유한 원소들의 집합입니다.
using System.Collections.Generic; HashSet<타입> 해시셋이름 = new HashSet<타입>(); // 예시 HashSet<int> uniqueNumbers = new HashSet<int> {1, 2, 2, 3, 4, 4, 5};
(4) Queue
선입선출(FIFO, First-In-First-Out) 방식의 데이터 구조입니다.
using System.Collections.Generic; Queue<타입> 큐이름 = new Queue<타입>(); // 예시 Queue<string> queue = new Queue<string>();
(5) Stack
선입후출(LIFO, Last-In-First-Out) 방식의 데이터 구조입니다.
using System.Collections.Generic; Stack<타입> 스택이름 = new Stack<타입>(); // 예시 Stack<string> stack = new Stack<string>();
6. 함수(메서드)
함수는 코드의 재사용성을 높이고, 프로그램의 구조를 개선하는데 사용되며, C#에서는 메서드라고도 불립니다.
메서드는 코드를 간결하게 만들고, 중복을 줄여주며, 프로그램의 가독성과 유지보수성을 높이는 데 도움이 되며, 반환 타입, 메서드 이름, 매개변수 리스트, 그리고 실행할 코드 블록으로 구성됩니다.
다음은 메서드 선언의 기본 구조입니다.
접근_지정자 반환_타입 메서드_이름(매개변수_타입 매개변수_이름, ...) { // 실행할 코드 return 반환값; // 반환 타입이 void가 아닌 경우에만 필요 }
예를 들어, 두 정수를 더하는 간단한 메서드를 작성해 보겠습니다.
public int Add(int a, int b) { int sum = a + b; return sum; }
여기에서 public은 접근 지정자이며, 이 메서드가 외부에서 접근 가능하다는 것을 나타냅니다. 반환 타입은 int이고, 메서드 이름은 Add입니다. 이 메서드는 두 개의 정수 매개변수를 받아 더한 값을 반환합니다.
메서드를 호출하여 사용하려면 다음과 같이 작성합니다.
int result = Add(3, 4); // result 값은 7이 됩니다.
7. 클래스와 객체
클래스를 사용하여 객체를 정의하고 생성할 수 있으며, 클래스는 속성과 메서드를 포함하고, 객체는 클래스의 인스턴스입니다.
C#은 객체 지향 프로그래밍 언어로서, 클래스와 객체가 핵심 개념입니다.
1) 클래스 (Class)
클래스는 객체를 생성하기 위한 틀 또는 설계도입니다. 클래스는 객체의 속성과 동작을 정의하며, 변수와 메서드로 구성됩니다. 변수는 객체의 상태를 나타내는 속성을 저장하고, 메서드는 객체의 동작을 정의하는 함수입니다.
클래스는 다음과 같은 형태로 선언합니다.
접근지정자 class 클래스이름 { // 필드, 프로퍼티, 메서드 등 }
예를 들어, 간단한 'Person' 클래스를 만들어 보면 다음과 같습니다.
public class Person { // 필드 public string Name; public int Age; // 메서드 public void SayHello() { Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old."); } }
2) 객체 (Object)
객체는 클래스를 기반으로 실제 메모리에 할당된 인스턴스입니다. 클래스를 사용하여 객체를 생성하면, 해당 클래스의 변수와 메서드가 메모리에 할당되어 실제로 사용할 수 있게 됩니다.
객체는 다음과 같은 형태로 생성합니다.
클래스이름 객체이름 = new 클래스이름();
위에서 정의한 'Person' 클래스를 기반으로 객체를 생성하고 사용해 보겠습니다.
Person person1 = new Person(); person1.Name = "Alice"; person1.Age = 30; person1.SayHello(); // 출력: Hello, my name is Alice and I am 30 years old.
이렇게 클래스와 객체를 사용하여 프로그램의 구조를 정의하고, 상태와 동작을 캡슐화하여 코드의 재사용성과 유지보수성을 높일 수 있습니다. 객체 지향 프로그래밍의 주요 개념인 캡슐화, 상속, 다형성 등은 클래스와 객체를 기반으로 구현됩니다.
8. 상속과 다형성
C#에서 상속과 다형성은 객체 지향 프로그래밍의 핵심 개념입니다.
1) 상속 (Inheritance)
상속은 기존의 클래스를 확장하여 새로운 클래스를 생성하는 과정입니다. 새로운 클래스는 기존 클래스의 속성과 메서드를 물려받아 사용할 수 있습니다. 이를 통해 코드의 중복을 줄이고 재사용성을 높일 수 있습니다. 상속은 기본 클래스(base class, 부모 클래스, 슈퍼 클래스)와 파생 클래스(derived class, 자식 클래스, 서브 클래스)의 관계를 통해 이루어집니다.
상속은 다음과 같이 구현합니다.
class DerivedClass : BaseClass { // 추가 필드, 프로퍼티, 메서드 등 }
예를 들어, 앞서 7. 클래스와 객체에서 만든 'Person' 클래스를 상속받아 'Employee' 클래스를 생성해 보겠습니다.
public class Employee : Person { public string Position; public void Work() { Console.WriteLine($"{Name} is working as a {Position}."); } }
이제 'Employee' 객체를 생성하면 'Person' 클래스의 속성과 메서드를 사용할 수 있습니다.
Employee employee = new Employee(); employee.Name = "Bob"; employee.Age = 28; employee.Position = "Software Developer"; employee.SayHello(); // 출력: Hello, my name is Bob and I am 28 years old. employee.Work(); // 출력: Bob is working as a Software Developer.
2) 다형성 (Polymorphism)
다형성은 상속 관계에 있는 클래스들의 인스턴스를 동일한 방식으로 사용할 수 있게 해주는 개념입니다. 다형성을 통해 유연한 코드 작성이 가능하며, 메서드 오버라이딩(overriding)과 메서드 오버로딩(overloading)이 다형성을 구현하는 방법입니다.
(1) 메서드 오버라이딩
파생 클래스에서 기본 클래스의 메서드를 재정의하여 사용하는 방법입니다. 이를 통해 기본 클래스의 기능을 확장하거나 변경할 수 있습니다.
public class Employee : Person { public string Position; // SayHello 메서드 오버라이딩 public override void SayHello() { Console.WriteLine($"Hello, my name is {Name}, I am {Age} years old and I work as a {Position}."); } public void Work() { Console.WriteLine($"{Name} is working as a {Position}."); } }
(2) 메서드 오버로딩
같은 이름의 메서드를 매개변수의 유형과 개수를 다르게 하여 여러 개로 선언하는 방법입니다. 이를 통해 하나의 메서드 이름으로 다양한 입력을 처리할 수 있습니다.
public class MyClass { public void PrintInfo(int number) { Console.WriteLine($"Integer: {number}"); } public void PrintInfo(string text) { Console.WriteLine($"String: {text}"); } public void PrintInfo(int number, string text) { Console.WriteLine($"Integer: {number}, String: {text}"); } }
이제 MyClass의 인스턴스를 생성하고 메서드 오버로딩을 사용해 보겠습니다.
MyClass myClass = new MyClass(); myClass.PrintInfo(42); // 출력: Integer: 42 myClass.PrintInfo("Hello"); // 출력: String: Hello myClass.PrintInfo(42, "Hello"); // 출력: Integer: 42, String: Hello
다형성을 통해 객체들을 일관된 방식으로 다룰 수 있으며, 확장성과 유연성이 향상되어 코드의 재사용성과 유지보수성이 높아집니다. 상속과 다형성을 활용하면 객체 지향 프로그래밍의 핵심 원칙인 캡슐화, 상속, 다형성을 충실히 따르는 코드를 작성할 수 있습니다.
9. 인터페이스(interface)
인터페이스는 C#에서 추상화를 구현하는 또 다른 방법이며, 클래스나 구조체가 구현해야 하는 메서드, 속성, 이벤트를 정의하며, 다중 상속을 지원합니다.
인터페이스는 메서드, 프로퍼티, 인덱서, 이벤트 등의 멤버를 포함할 수 있지만, 구현을 포함할 수 없습니다.
인터페이스는 클래스나 구조체에 정의된 규약을 명시하며, 인터페이스를 구현한 클래스나 구조체는 해당 인터페이스에 선언된 모든 멤버를 구현해야 합니다. 이를 통해 클래스간의 의존성을 낮추고, 높은 수준의 추상화를 구현할 수 있습니다.
인터페이스는 다중 상속을 지원하므로, 하나의 클래스가 여러 인터페이스를 구현할 수 있습니다. 이는 다양한 구현을 갖는 객체들을 동일한 인터페이스를 통해 처리할 수 있으므로, 코드의 재사용성과 유연성이 향상됩니다.
인터페이스를 선언하는 방법은 다음과 같습니다.
접근지정자 interface 인터페이스이름 { // 메서드, 프로퍼티, 인덱서, 이벤트 등의 선언 }
예를 들어, 'IDrivable' 인터페이스를 선언해 보겠습니다.
public interface IDrivable { int Speed { get; set; } void Accelerate(); void Brake(); }
이제 'Car' 클래스를 만들어 'IDrivable' 인터페이스를 구현해 보겠습니다.
public class Car : IDrivable { public int Speed { get; set; } public void Accelerate() { Speed += 5; } public void Brake() { Speed -= 5; } }
10. 추상클래스 abstract와 인터페이스 비교
abstract와 인터페이스는 C#에서 추상화를 구현하는 방법입니다. 두 개념은 유사한 면이 있지만, 목적과 사용 방법에 차이가 있습니다.
1) abstract (추상 클래스)
추상 클래스는 기본 기능을 제공하면서 파생 클래스에서 구현해야 하는 기능을 명시하는 클래스입니다. 추상 클래스는 추상 메서드(구현이 없는 메서드)와 일반 메서드를 포함할 수 있습니다. 추상 클래스를 상속받는 파생 클래스는 추상 메서드를 반드시 구현해야 합니다. 추상 클래스는 인스턴스를 생성할 수 없으며, 상속을 통해서만 사용할 수 있습니다.
2) 비교
▶ 상속 관계: 추상 클래스는 단일 상속만 지원하며, 인터페이스는 다중 상속을 지원합니다.
▶ 멤버 구현: 추상 클래스는 추상 메서드와 일반 메서드를 포함할 수 있습니다. 인터페이스는 구현이 없는 멤버만 포함할 수 있습니다.
▶ 인스턴스 생성: 추상 클래스와 인터페이스 모두 인스턴스를 직접 생성할 수 없습니다. 상속이나 구현을 통해서만 사용할 수 있습니다.
▶ 구현 방법: 추상 클래스는 파생 클래스에서 override 키워드를 사용하여 추상 메서드를 구현합니다. 인터페이스는 구현하는 클래스에서 인터페이스의 멤버를 직접 구현합니다.
3) 사용 시 고려할 점
▶ 기본 기능을 제공하고, 일부 공통 기능을 파생 클래스에서 구현하려면 추상 클래스를 사용합니다.
▶ 여러 클래스나 구조체가 동일한 기능을 갖도록 강제하려면 인터페이스를 사용합니다.
▶ 다중 상속을 필요로 하는 경우 인터페이스를 사용합니다.
각각의 상황에 따라 추상 클래스와 인터페이스를 적절히 활용하여 프로그램의 구조를 추상화하고, 코드의 재사용성과 유연성을 높일 수 있어야 합니다.
11. 예외 처리 (exception handling)
예외 처리는 프로그램 실행 중에 발생할 수 있는 오류나 예외 상황에 대응하는 방법입니다. C#에서는 try, catch, finally 및 throw 키워드를 사용하여 예외 처리를 구현합니다. 이를 통해 프로그램의 안정성과 가독성을 높일 수 있습니다.
1) try-catch
예외가 발생할 가능성이 있는 코드를 try 블록 내에 작성합니다. 예외가 발생하면 해당 예외를 처리하는 catch 블록이 실행됩니다. 각각의 try 블록은 여러 개의 catch 블록을 가질 수 있으며, 이를 통해 다양한 예외 유형을 처리할 수 있습니다.
try { // 예외가 발생할 가능성이 있는 코드 } catch (ExceptionType1 ex) { // ExceptionType1 예외 처리 코드 } catch (ExceptionType2 ex) { // ExceptionType2 예외 처리 코드 }
2) finally
finally 블록은 try와 catch 블록이 실행된 후 항상 실행되는 블록입니다. 이 블록은 주로 리소스 정리, 파일 닫기 등의 작업을 수행하는 데 사용됩니다.
try { // 예외가 발생할 가능성이 있는 코드 } catch (Exception ex) { // 예외 처리 코드 } finally { // 항상 실행되는 코드 (리소스 정리 등) }
3) throw
프로그램에서 예외를 발생시키려면 throw 키워드를 사용합니다. 이를 통해 사용자 정의 예외를 만들고 해당 예외를 처리할 수 있습니다.
if (condition) { throw new CustomException("Custom exception message."); }
예외 처리를 사용하면 프로그램의 안정성을 높이고, 오류 발생 시 적절한 대응을 할 수 있습니다. 또한, 예외 처리를 통해 오류 정보를 수집하고 분석하여 프로그램을 개선할 수 있습니다.
12. 네임스페이스(namespace)와 using 지시문
네임스페이스는 C#에서 관련된 형식(클래스, 구조체, 인터페이스, 열거형 등)을 그룹화하는 방법입니다. 네임스페이스를 사용하면 동일한 이름의 형식이 충돌하는 것을 방지할 수 있으며, 코드의 구조를 논리적으로 구분할 수 있습니다. 네임스페이스는 중첩될 수 있습니다.
네임스페이스를 선언하는 방법은 다음과 같습니다.
namespace NamespaceName { // 클래스, 구조체, 인터페이스, 열거형 등의 선언 }
예를 들어, "MyApplication"이라는 네임스페이스를 선언하고, "MyClass"라는 클래스를 포함시키는 방법은 다음과 같습니다.
namespace MyApplication { public class MyClass { // MyClass의 구현 } }
using 지시문은 C#에서 네임스페이스를 사용하기 위한 방법입니다. using 지시문을 사용하면 해당 네임스페이스에 정의된 형식을 완전한 이름 없이 사용할 수 있습니다. 이를 통해 코드의 가독성을 높일 수 있습니다.
예를 들어, 다음과 같이 using 지시문을 사용하여 "System" 네임스페이스를 포함할 수 있습니다.
using System; namespace MyApplication { public class MyClass { public void Print() { // System 네임스페이스의 Console 클래스를 완전한 이름 없이 사용 Console.WriteLine("Hello, world!"); } } }
using 지시문을 사용하면 코드의 길이를 줄이고 가독성을 높일 수 있으나, 동일한 이름의 형식이 다른 네임스페이스에 있는 경우 충돌이 발생할 수 있습니다. 이런 경우, 형식을 사용할 때 완전한 이름(네임스페이스 포함)을 사용하여 명확히 구분해야 합니다.
13. 접근 제어자 (access modifier)
접근 제어자(access modifier)는 C#에서 클래스, 구조체, 인터페이스의 멤버(필드, 프로퍼티, 메서드 등)에 대한 접근 수준을 제한하는 방법입니다. 접근 제어자를 사용하면 캡슐화를 구현하여 객체의 상태와 행동을 보호하고, 코드의 재사용성과 유지 보수성을 높일 수 있습니다.
C#에서는 다음과 같은 접근 제어자를 사용할 수 있습니다.1) public
멤버가 선언된 형식 외부에서도 접근할 수 있습니다. 제한이 없는 가장 개방적인 접근 수준입니다.
public class MyClass { public int MyField; }
2) private
멤버가 선언된 형식 내에서만 접근할 수 있습니다. 가장 제한적인 접근 수준으로, 기본적으로 멤버에 접근 제어자를 지정하지 않으면 private로 간주됩니다.
class MyClass { private int MyField; }
3) protected
멤버가 선언된 형식 및 해당 형식을 상속받은 파생 형식 내에서만 접근할 수 있습니다. 이를 통해 하위 클래스에서 멤버를 사용하면서도 외부에서의 접근을 제한할 수 있습니다.
class MyBaseClass { protected int MyField; } class MyDerivedClass : MyBaseClass { public void AccessMyField() { MyField = 10; // MyBaseClass의 MyField에 접근 가능 } }
4) internal
멤버가 같은 어셈블리(프로젝트) 내에서만 접근할 수 있습니다. 다른 어셈블리에서는 접근할 수 없습니다.
internal class MyClass { internal int MyField; }
5) protected internal
멤버가 같은 어셈블리 내에서는 접근할 수 있고, 다른 어셈블리에서도 해당 형식을 상속받은 파생 형식 내에서 접근할 수 있습니다. protected와 internal의 조합입니다.
protected internal class MyClass { protected internal int MyField; }
6) private protected
멤버가 선언된 형식 및 해당 형식을 상속받은 파생 형식 내에서만 접근할 수 있으며, 같은 어셈블리 내에서만 해당 규칙이 적용됩니다. 다른 어셈블리의 파생 형식에서는 접근할 수 없습니다.
private protected class MyClass { private protected int MyField; }
접근 제어자를 적절히 사용하여 객체의 상태와 행동을 보호하고, 코드의 재사용성과 유지 보수성을 높일 수 있습니다. 접근 제어자의 사용 원칙은 다음과 같습니다.
1) 최소 권한 원칙(Principle of least privilege)
멤버의 접근 수준을 가능한 한 제한적으로 설정하여, 객체의 내부 상태와 행동을 외부로부터 보호합니다. 예를 들어, 클래스의 필드는 private로 선언하고, 필요한 경우 public이나 protected 프로퍼티를 통해 접근할 수 있도록 합니다.
2) 캡슐화(Encapsulation)
객체의 상태를 직접 변경하지 않고, 메서드를 통해 간접적으로 변경하도록 구현합니다. 이를 통해 객체의 상태 변경에 대한 검증과 처리를 한 곳에서 관리할 수 있습니다.
3) 상속(Inheritance)과 다형성(Polymorphism)
파생 클래스에서 기본 클래스의 멤버를 사용하려면 protected 접근 제어자를 사용합니다. 이를 통해 하위 클래스에서 멤버를 사용하면서도 외부에서의 접근을 제한할 수 있습니다.
4) 어셈블리 간의 의존성 관리
어셈블리 간의 의존성을 관리하기 위해 internal 접근 제어자를 사용하여 해당 어셈블리 내에서만 사용할 수 있는 멤버를 정의합니다.
접근 제어자를 효과적으로 사용하면 객체 지향 프로그래밍의 원칙에 따라 프로그램의 구조를 개선하고, 코드의 가독성, 재사용성, 유지 보수성을 높일 수 있습니다.
14. 가비지 컬렉션(Garbage Collection, GC)
가비지 컬렉션은 C#과 같은 관리되는 언어에서 사용되는 메모리 관리 기술입니다. 가비지 컬렉션은 프로그램에서 더 이상 사용되지 않는 객체를 자동으로 찾아내어 메모리를 해제하는 역할을 합니다. 이를 통해 개발자가 명시적으로 메모리를 할당하고 해제하는 데 신경 쓰지 않아도 되며, 메모리 누수(memory leak)와 같은 일반적인 문제를 방지할 수 있습니다.
C#에서는 가비지 컬렉션을 수행하기 위해 .NET의 가비지 컬렉터(Garbage Collector)가 사용됩니다.
1) 가비지 컬렉터의 작동 방식
▶ 힙(heap) 메모리에서 동적으로 할당된 객체를 추적합니다. 스택(stack) 메모리에 할당된 지역 변수와 값 형식은 가비지 컬렉션의 대상이 아닙니다.
▶ 객체의 참조가 더 이상 존재하지 않거나 사용되지 않는 경우, 해당 객체는 가비지로 간주됩니다.
▶ 가비지 컬렉터는 주기적으로 실행되며, 가비지로 간주되는 객체를 찾아내어 메모리를 해제합니다. 가비지 컬렉터는 일반적으로 시스템의 메모리 사용량이 높아질 때, 프로그램의 성능이 저하될 때 등에 실행됩니다.
▶ 가비지 컬렉션 과정에서 객체들이 메모리상에서 이동할 수 있습니다. 이를 통해 메모리의 단편화(fragmentation)를 줄이고 메모리 사용 효율을 높입니다.
2) 가비지 컬렉션의 장점
▶ 개발자가 메모리 관리에 대한 부담을 덜 수 있습니다.
▶ 메모리 누수를 방지하여 프로그램의 안정성을 높입니다.
▶ 메모리 단편화를 줄여 메모리 사용 효율을 높입니다.
3) 가비지 컬렉션의 단점
▶ 가비지 컬렉션 과정에서 프로그램의 성능이 일시적으로 저하될 수 있습니다.
▶ 가비지 컬렉션의 실행 시점을 개발자가 직접 제어할 수 없어 메모리 관리가 불투명해질 수 있습니다.
가비지 컬렉션은 메모리 관리를 효과적으로 수행하지만, 프로그램의 성능에 영향을 줄 수 있기 때문에 개발자는 가비지 컬렉션의 동작 방식을 이해하고 적절한 최적화를 수행해야 합니다.
4) 가비지 컬렉션과 관련된 일반적인 최적화 방법
▶ 객체 생성 최소화: 객체를 불필요하게 생성하지 않고 필요할 때만 생성하여 가비지 컬렉션의 부담을 줄입니다.
▶ 객체 풀링(Object Pooling): 빈번하게 생성되고 해제되는 객체를 재사용하기 위해 객체 풀을 사용합니다. 이를 통해 가비지 컬렉션의 실행 빈도를 줄일 수 있습니다.
▶ 대용량 객체 사용 최소화: 대용량 객체를 사용하면 가비지 컬렉션의 성능이 저하될 수 있으므로 필요한 경우에만 사용합니다.
▶ 참조 제거: 객체를 더 이상 사용하지 않을 때 참조를 제거하여 가비지 컬렉터가 쉽게 객체를 찾을 수 있도록 합니다. 예를 들어, 객체 참조를 null로 설정하거나 컬렉션에서 제거할 수 있습니다.
▶ IDisposable 인터페이스 구현: 객체가 사용하는 비관리 리소스(네트워크 연결, 파일 핸들 등)를 해제해야 하는 경우 IDisposable 인터페이스를 구현하고 Dispose 메서드에서 리소스를 해제합니다. 이렇게 하면 개발자가 명시적으로 리소스를 해제할 수 있으며, 가비지 컬렉션에 의존하지 않아도 됩니다.
▶ 가비지 컬렉션 알고리즘 이해: 가비지 컬렉터의 성능을 최적화하려면 가비지 컬렉션 알고리즘을 이해하고 적절한 최적화를 수행해야 합니다. 예를 들어, .NET의 가비지 컬렉터는 세대(generation) 기반 알고리즘을 사용하므로 객체의 생존 시간을 고려한 최적화를 수행할 수 있습니다.
가비지 컬렉션은 메모리 관리를 자동화하지만 프로그램의 성능에 영향을 줄 수 있으므로, 개발자는 가비지 컬렉션의 동작 방식을 이해하고 적절한 최적화를 수행해야 합니다. 이렇게 하면 프로그램의 메모리 사용 효율과 성능을 높일 수 있습니다.
15. LINQ(Language Integrated Query)
C#에서는 데이터를 검색하고 변환하는 데 사용되는 강력한 질의 기능인 LINQ를 제공합니다. 이를 통해 SQL 스타일의 쿼리를 사용하여 배열, 컬렉션, XML, 데이터베이스 등의 데이터 소스를 처리할 수 있습니다.
LINQ를 사용하면 데이터에 대한 쿼리를 쉽고 직관적인 방식으로 작성할 수 있으며, 데이터 원본에 관계없이 일관된 쿼리 구문을 사용할 수 있습니다. LINQ는 SQL과 비슷한 구문을 사용하지만, C#의 강력한 기능과 결합되어 더욱 풍부한 쿼리 및 변환 기능을 제공합니다.
▶ LINQ는 다양한 데이터 원본에 대해 쿼리를 수행할 수 있습니다. 주요 LINQ 프로바이더는 다음과 같습니다.
▶ LINQ to Objects: 메모리에 존재하는 객체 컬렉션(배열, 리스트 등)에 대해 쿼리를 수행합니다.▶ LINQ to SQL: SQL Server 데이터베이스에 대해 쿼리를 수행하고 객체-관계 매핑을 지원합니다.
▶ LINQ to XML: XML 문서에 대해 쿼리를 수행합니다.
▶ LINQ to Entities: Entity Framework를 사용하여 다양한 데이터베이스에 대해 쿼리를 수행합니다.
LINQ 쿼리는 다음과 같은 구성 요소로 이루어져 있습니다.
▶ 데이터 소스(Data Source): 쿼리를 수행할 데이터 원본입니다. 예를 들어, 배열이나 리스트와 같은 컬렉션을 사용할 수 있습니다.
▶ 쿼리 연산자(Query Operator): 데이터 소스에 대해 수행할 작업을 정의하는 연산자입니다. 예를 들어, 선택(Select), 필터링(Where), 정렬(OrderBy) 등의 연산자를 사용할 수 있습니다.
▶ 쿼리 표현식(Query Expression): 쿼리 연산자를 사용하여 데이터 소스에 대한 작업을 정의하는 실제 구문입니다.
▶ 결과(Result): 쿼리를 실행하여 얻은 결과입니다. 결과는 즉시 반환되거나 지연된 평가(deferred execution)를 사용하여 나중에 반환될 수 있습니다.
LINQ 쿼리 예시
// 데이터 소스 int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // 쿼리 표현식 var evenNumbers = from number in numbers where number % 2 == 0 orderby number select number; // 결과 출력 foreach (var number in evenNumbers) { Console.WriteLine(number); }
LINQ는 C#에서 데이터 처리를 쉽고 강력하게 수행할 수 있도록 도와주며, 다양한 데이터 원본에 대한 일관된 쿼리 구문을 제공합니다. 이를 통해 개발자의 생산성과 코드의 가독성을 향상시킬 수 있습니다.
LINQ를 사용하면 다음과 같은 장점이 있습니다.
▶ 직관적인 구문: LINQ는 SQL과 비슷한 구문을 사용하여 데이터 처리를 직관적으로 작성할 수 있습니다. 이를 통해 개발자가 데이터 처리 로직을 이해하고 작성하기 쉽습니다.
▶ 데이터 원본에 독립적: LINQ는 데이터 원본에 관계없이 일관된 쿼리 구문을 제공합니다. 이를 통해 개발자가 다양한 데이터 원본에 대한 처리 로직을 쉽게 작성할 수 있습니다.
▶ 강력한 쿼리 기능: LINQ는 C#의 강력한 기능을 활용하여 풍부한 쿼리 기능을 제공합니다. 예를 들어, 람다 식을 사용하여 간결하고 표현력 있는 쿼리를 작성할 수 있습니다.
▶ 타입 안전성(Type Safety): LINQ는 컴파일 시점에 타입 검사를 수행하여 타입 관련 오류를 미리 발견할 수 있습니다. 이를 통해 개발자가 안전하게 코드를 작성하고 실행할 수 있습니다.
▶ 확장성: LINQ는 사용자 정의 쿼리 연산자를 만들어 확장할 수 있습니다. 이를 통해 개발자가 특정 데이터 원본이나 처리 방식에 대한 사용자 정의 쿼리 기능을 쉽게 추가할 수 있습니다.
LINQ를 사용하여 다양한 데이터 원본과 처리 방식에 대한 통합된 쿼리 인터페이스를 제공할 수 있으며, 개발자의 생산성과 코드의 가독성을 향상시킬 수 있습니다. 이를 통해 C#에서 데이터 처리를 간단하고 효과적으로 수행할 수 있습니다.
16. 람다 (lambda)
람다는 C#에서 익명 함수(anonymous function)를 표현하는 간결한 구문입니다. 람다 식은 입력 매개 변수를 가지며, 이를 통해 결과를 계산하여 반환합니다. 람다는 주로 LINQ 쿼리, 이벤트 처리, 비동기 프로그래밍 등에서 사용되며, 일회성 함수를 간결하게 표현할 수 있습니다.
람다 식의 기본 구조는 다음과 같습니다.
(parameters) => expression
여기서 parameters는 입력 매개 변수를 나열한 것이며, expression은 계산할 표현식입니다.
예를 들어, 두 수를 더하는 람다 식은 다음과 같습니다.
(x, y) => x + y
C#에서 람다 식은 Func 또는 Action 델리게이트 형식과 호환됩니다.
예를 들어, 위의 람다 식은 다음과 같이 Func<int, int, int> 델리게이트에 할당할 수 있습니다.
Func<int, int, int> add = (x, y) => x + y; int sum = add(3, 4); // 결과는 7
또한, 람다 식은 LINQ 쿼리에서 사용되어 간결한 쿼리 표현식을 작성할 수 있습니다.
예를 들어, 정수 배열에서 짝수만 선택하는 쿼리는 다음과 같이 작성할 수 있습니다.
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var evenNumbers = numbers.Where(n => n % 2 == 0);
람다 식은 코드의 가독성을 향상시키며, 일회성 함수를 간결하게 작성할 수 있도록 합니다. 또한, C#의 기능과 결합하여 강력한 프로그래밍 기법을 제공합니다. 이를 통해 개발자는 더욱 효과적으로 코드를 작성하고 이해할 수 있습니다.
17. 비동기 프로그래밍
비동기 프로그래밍은 애플리케이션의 작업을 병렬로 실행하여 전체 성능과 반응성을 향상시키는 프로그래밍 패러다임입니다. 비동기 작업을 사용하면, 오랜 시간이 걸리는 작업을 실행할 때 애플리케이션의 주 스레드를 차단하지 않고 다른 작업을 계속 수행할 수 있습니다.
1) Task
C#에서 Task는 비동기 작업을 나타내는 클래스입니다. Task는 System.Threading.Tasks 네임스페이스에 포함되어 있으며, 비동기 프로그래밍에서 작업을 나타내고 관리하는 데 사용됩니다. Task를 사용하면 오랜 시간이 걸리는 작업을 병렬로 실행하여 애플리케이션의 전체 성능과 반응성을 향상시킬 수 있습니다.
Task 클래스는 다음과 같은 주요 기능을 제공합니다.
▶ 비동기 작업의 상태를 나타냅니다. Task 객체는 작업의 상태를 나타내는 IsCompleted, IsCanceled, IsFaulted 등의 속성을 제공합니다.
▶ 비동기 작업의 결과를 반환합니다. Task<TResult> 형식을 사용하여 작업의 결과를 반환할 수 있습니다. 여기서 TResult는 반환되는 결과의 형식입니다.
▶ 비동기 작업의 예외를 처리합니다. Task 객체는 작업 중 발생한 예외를 Exception 속성에 저장하고, Task를 기다리는 코드에서 이를 처리할 수 있게 합니다.
▶ 비동기 작업을 취소합니다. Task 객체는 작업의 취소를 위해 CancellationToken을 사용하여 작업을 취소할 수 있게 합니다.
Task를 사용하는 일반적인 시나리오는 다음과 같습니다.
▶ async와 await 키워드를 사용하여 비동기 메서드를 작성하고 호출합니다.
▶ Task.Factory.StartNew 또는 Task.Run 메서드를 사용하여 별도의 스레드에서 작업을 실행합니다.
▶ Task.Wait, Task.WaitAll, Task.WaitAny 등의 메서드를 사용하여 하나 이상의 비동기 작업이 완료될 때까지 기다립니다.
2) 키워드 설명
(1) async
이 키워드는 메서드에 비동기 작업을 수행한다는 것을 나타냅니다. async 키워드는 반환형 앞에 사용하며, 반환형은 Task 또는 Task<TResult> 형식이어야 합니다. 여기서 TResult는 반환되는 결과의 형식입니다.
(2) await
이 키워드는 비동기 작업이 완료될 때까지 기다린 다음 결과를 반환합니다. await는 async 메서드 내에서만 사용할 수 있으며, Task 또는 Task<TResult> 객체 앞에 사용됩니다.
3) 비동기 프로그래밍 예시
using System; using System.Net.Http; using System.Threading.Tasks; public class AsyncExample { public static async Task Main() { var url = "https://example.com/"; var content = await DownloadStringAsync(url); Console.WriteLine(content); } public static async Task<string> DownloadStringAsync(string url) { using HttpClient httpClient = new HttpClient(); return await httpClient.GetStringAsync(url); } }
위의 예시에서 DownloadStringAsync 메서드는 async 키워드를 사용하여 비동기 작업을 수행하며, 결과로 Task<string> 형식을 반환합니다. Main 메서드에서는 await 키워드를 사용하여 DownloadStringAsync 메서드의 작업이 완료될 때까지 기다린 후 결과를 출력합니다.
C#의 Task 클래스는 비동기 프로그래밍에서 작업을 나타내고 관리하는 강력한 도구입니다. 이를 통해 개발자는 애플리케이션의 성능과 반응성을 향상시키는 데 도움이 되는 비동기 작업을 쉽게 구현할 수 있습니다.
18. 델리게이트(delegate)와 이벤트(event)
델리게이트와 이벤트는 C#에서 메서드를 참조하고 이벤트 기반 프로그래밍을 지원하는 중요한 개념입니다.
델리게이트는 메서드에 대한 참조를 저장하는 객체로, 이벤트 기반 프로그래밍과 콜백 메서드를 구현하는 데 사용됩니다. 이벤트는 발생할 때마다 특정 액션을 수행하도록 구독한 메서드를 호출하는 데 사용되는 델리게이트의 일종입니다.
1) 델리게이트
델리게이트는 메서드를 참조하는 형식입니다. 다시 말해, 델리게이트는 메서드에 대한 참조를 저장하고 해당 메서드를 호출할 수 있습니다. 델리게이트는 메서드 시그니처와 일치하는 메서드를 참조할 수 있습니다. 델리게이트는 일종의 메서드 포인터와 비슷한 역할을 하지만, 타입 안전성을 제공하는 C#의 특성을 유지합니다.
예를 들어, 두 개의 int를 받아 int를 반환하는 메서드를 참조하는 델리게이트를 선언할 수 있습니다.
public delegate int MyDelegate(int a, int b);
2) 이벤트
이벤트는 델리게이트를 기반으로 한 특별한 형식으로, 이벤트 기반 프로그래밍을 지원합니다. 이벤트는 객체가 발생시키며, 다른 객체는 이 이벤트를 구독하고 이벤트가 발생할 때마다 알림을 받습니다. 이벤트는 특정 상황(예: 버튼 클릭, 타이머 종료 등)에서 코드를 실행할 수 있도록 해줍니다.
이벤트를 선언하고 사용하기 위해 다음 단계를 따릅니다.
▶ 델리게이트 형식을 선언합니다.
▶ 이벤트를 선언하고 델리게이트 형식을 사용합니다.
▶ 이벤트를 발생시키는 메서드를 작성하고 이벤트를 호출합니다.
▶ 이벤트 핸들러 메서드를 작성하고 이벤트에 구독합니다.
예를 들어, 간단한 이벤트를 선언하고 사용하는 방법은 다음과 같습니다.
// 델리게이트 선언 public delegate void MyEventHandler(); // 이벤트 선언 public event MyEventHandler MyEvent; // 이벤트를 발생시키는 메서드 public void RaiseEvent() { MyEvent?.Invoke(); } // 이벤트 핸들러 메서드 public void EventHandler() { Console.WriteLine("이벤트가 발생했습니다."); } // 이벤트 구독 및 사용 public void SubscribeAndUseEvent() { MyEvent += EventHandler; // 이벤트 핸들러 메서드를 이벤트에 구독 RaiseEvent(); // 이벤트 발생 }
델리게이트와 이벤트는 C#에서 메서드 참조와 이벤트 기반 프로그래밍을 지원하는 데 중요한 역할을 합니다. 이를 통해 애플리케이션의 다양한 컴포넌트들이 서로 독립적으로 동작하면서 특정 상황에서 서로 상호작용할 수 있습니다.
델리게이트와 이벤트를 사용하는 몇 가지 추가적인 개념은 다음과 같습니다.
(1) 멀티캐스팅 (Multicasting)
델리게이트는 여러 개의 메서드 참조를 함께 저장할 수 있습니다. 이를 멀티캐스팅이라고 합니다. 이렇게 하면 델리게이트를 호출할 때 참조된 모든 메서드가 순차적으로 실행됩니다. 이 기능은 이벤트에서 특히 유용하며, 여러 개의 이벤트 핸들러가 동일한 이벤트를 구독할 수 있습니다.
예를 들어, 두 개의 이벤트 핸들러를 동일한 이벤트에 구독시킬 수 있습니다.
public void EventHandler1() { Console.WriteLine("이벤트 핸들러 1이 호출되었습니다."); } public void EventHandler2() { Console.WriteLine("이벤트 핸들러 2가 호출되었습니다."); } public void SubscribeAndUseEvent() { MyEvent += EventHandler1; // 이벤트 핸들러 1 구독 MyEvent += EventHandler2; // 이벤트 핸들러 2 구독 RaiseEvent(); // 이벤트 발생 시 두 핸들러가 순차적으로 호출됩니다. }
(2) 이벤트 액세서 (Event Accessors)
이벤트 액세서는 이벤트의 구독 및 해지를 사용자 지정할 수 있게 합니다. add 및 remove 키워드를 사용하여 액세서를 정의할 수 있습니다. 이 기능은 이벤트를 더욱 유연하게 사용할 수 있게 해줍니다.
예를 들어, 사용자 지정 이벤트 액세서를 정의하는 방법은 다음과 같습니다.
private MyEventHandler _myEvent; public event MyEventHandler MyEvent { add { Console.WriteLine("이벤트에 구독 중입니다."); _myEvent += value; } remove { Console.WriteLine("이벤트 구독 해지 중입니다."); _myEvent -= value; } }
이벤트 기반 프로그래밍은 특히 사용자 인터페이스(UI) 개발에서 널리 사용되며, 다양한 UI 요소(예: 버튼, 텍스트 박스 등)가 서로 독립적으로 동작하면서 특정 상황에서 상호작용할 수 있게 해줍니다. 또한, 이벤트 기반 프로그래밍은 시스템 간의 통신이나 애플리케이션 내부의 다양한 컴포넌트들이 서로 소통하고 협력하는 데 필요한 유연성과 확장성을 제공합니다.
델리게이트와 이벤트를 사용하여 작성된 코드는 다음과 같은 이점을 가집니다.
▶ 결합도(Coupling) 감소: 이벤트를 사용하면 소스 객체와 대상 객체 간의 결합도가 낮아져 코드의 유지 보수가 쉬워집니다. 이벤트 기반의 코드는 새로운 기능을 추가하거나 기존 기능을 변경할 때 코드의 변경이 최소화됩니다.
▶ 코드 재사용성 향상: 델리게이트를 사용하여 메서드를 인수로 전달할 수 있으므로, 유사한 작업을 수행하는 다양한 메서드를 쉽게 재사용할 수 있습니다.
▶ 확장성 향상: 이벤트를 사용하면 애플리케이션의 구성 요소를 쉽게 추가하거나 제거할 수 있으며, 이벤트 핸들러를 동적으로 구독하고 해지할 수 있습니다.
▶ 응용 프로그램 로직의 명확성: 이벤트 기반 프로그래밍을 사용하면 애플리케이션의 주요 로직을 이벤트 핸들러에 포함시켜 코드의 가독성과 이해도가 향상됩니다.
델리게이트와 이벤트를 활용하면 효과적인 이벤트 기반 프로그래밍을 구현할 수 있어, 애플리케이션의 유지 보수성, 재사용성, 확장성 및 가독성을 크게 향상시킬 수 있습니다. 이러한 이점 덕분에 델리게이트와 이벤트는 C# 및 .NET 프레임워크에서 중요한 프로그래밍 기법인 것으로 간주됩니다.
19. 프레임워크와 라이브러리
프레임워크와 라이브러리는 소프트웨어 개발에서 중요한 역할을 하는데, 둘 다 코드의 재사용성을 높이고 개발 시간을 단축하는 데 도움을 줍니다. 그러나 두 개념은 몇 가지 중요한 차이점이 있습니다.
1) 라이브러리 (Library)
라이브러리는 재사용 가능한 코드의 모음으로, 개발자가 필요한 기능을 쉽게 사용할 수 있도록 미리 작성된 함수, 클래스, 데이터 구조 등으로 구성되어 있습니다. 라이브러리는 일반적으로 특정 목적을 위해 설계되며, 개발자는 라이브러리를 선택하여 프로젝트에 포함시켜 해당 라이브러리의 기능을 사용할 수 있습니다. 라이브러리는 개발자의 코드가 주도하는 방식으로 작동합니다.
예를 들어, 수학 계산, 파일 처리, 네트워킹 등에 대한 다양한 라이브러리를 사용하여 개발자는 빠르게 애플리케이션을 구축할 수 있습니다.
2) 프레임워크 (Framework)
프레임워크는 소프트웨어 개발에 필요한 기본 구조를 제공하는 것으로, 프로그램의 구조와 흐름을 정의하는 일련의 규칙, 가이드라인 및 도구를 포함합니다. 프레임워크는 '제어의 역전(Inversion of Control, IoC)' 원칙을 따르며, 프레임워크 자체가 애플리케이션의 흐름을 제어합니다. 개발자는 프레임워크에 정의된 특정 포인트에 사용자 정의 코드를 작성하여 프레임워크가 호출하게 됩니다.
예를 들어, 웹 개발에서 사용되는 ASP.NET이나 Ruby on Rails와 같은 프레임워크는 개발자가 웹 애플리케이션을 빠르게 구축할 수 있도록 도와주는 동시에, 일관된 아키텍처와 디자인 패턴을 제공합니다.
요약하면, 라이브러리는 코드의 모음이며, 개발자가 필요에 따라 선택적으로 사용할 수 있습니다.프레임워크는 애플리케이션의 기본 구조를 제공하며, 개발자는 프레임워크의 규칙과 가이드라인에 따라 코드를 작성해야 합니다. 라이브러리와 프레임워크는 서로 보완재로 작용하여 개발 프로세스를 향상시키고 더 효율적인 개발을 가능하게 합니다.
3) 라이브러리의 특징과 장점▶ 코드 재사용성: 라이브러리는 특정 기능을 구현한 코드를 모아놓은 것이므로, 라이브러리를 사용함으로써 코드를 재사용할 수 있습니다.
▶ 개발 시간 단축: 라이브러리에 포함된 코드를 사용하면 개발 시간을 크게 줄일 수 있습니다.
▶ 유지 보수 용이: 라이브러리는 일반적으로 잘 정리되어 있으며, 라이브러리 자체가 업데이트되면 해당 변경 사항이 애플리케이션 전체에 적용됩니다.
4) 프레임워크의 특징과 장점
▶ 일관된 아키텍처: 프레임워크는 개발자에게 일관된 아키텍처와 디자인 패턴을 제공하여 코드의 가독성과 유지 보수성을 높입니다.
▶ 개발 시간 단축: 프레임워크를 사용하면 개발 시간을 줄일 수 있습니다. 프레임워크는 개발자가 반복적으로 작성해야 하는 기본적인 코드를 제공하기 때문입니다.
▶ 표준화: 프레임워크는 표준화된 방식으로 개발을 진행할 수 있게 합니다. 이로 인해 팀 내에서 일관된 코드 스타일과 프로젝트 구조를 유지할 수 있습니다.
▶ 개발자는 프로젝트의 요구 사항과 목표에 따라 라이브러리와 프레임워크를 적절히 선택하여 사용할 수 있습니다. 프로젝트의 복잡성, 개발 기간, 팀의 기술 레벨 등을 고려하여 최적의 결정을 내릴 수 있어야 합니다. 라이브러리와 프레임워크의 적절한 조합은 개발 프로세스의 효율성을 높이고 개발 시간을 줄여주는 데 큰 도움이 됩니다.
20. C#에서 사용되는 프레임워크
C#에서 사용되는 프레임워크는 주로 .NET 프레임워크와 .NET Core로 나뉩니다. 이 두 프레임워크는 모두 C#을 사용한 애플리케이션 개발에 필요한 도구, 라이브러리 및 기본 구조를 제공합니다. 하지만 두 프레임워크의 차이점과 특징을 이해하는 것이 중요합니다.
1) .NET 프레임워크
.NET 프레임워크는 마이크로소프트가 개발한 일반적인 목적의 소프트웨어 개발 플랫폼으로, 2000년대 초에 처음 출시되었습니다. 이 프레임워크는 주로 윈도우 운영 체제에서 실행되는 애플리케이션을 개발하는 데 사용됩니다. .NET 프레임워크는 C#, Visual Basic .NET, F# 등 다양한 프로그래밍 언어를 지원하며, 다양한 유형의 애플리케이션 개발을 지원합니다. 예를 들어, Windows Forms, WPF(Windows Presentation Foundation), ASP.NET 등을 사용하여 데스크톱 애플리케이션, 웹 애플리케이션 및 서비스를 개발할 수 있습니다.
2) .NET Core
.NET Core는 .NET 프레임워크의 오픈 소스 및 크로스 플랫폼 버전으로, 2016년에 처음 출시되었습니다. .NET Core는 윈도우, 리눅스 및 macOS에서 실행되는 애플리케이션 개발을 지원하며, 높은 성능과 확장성을 제공합니다. .NET Core는 마이크로서비스 아키텍처와 도커 컨테이너 기술과 같은 최신 개발 트렌드에 적합합니다. .NET Core는 ASP.NET Core, Entity Framework Core 등의 모듈을 사용하여 웹 애플리케이션, RESTful 서비스 및 데스크톱 애플리케이션을 개발할 수 있습니다.
3) . NET
2020년에는 .NET 5가 출시되었으며, 이후 .NET 6도 출시되었습니다. 이들은 .NET 프레임워크와 .NET Core의 장점을 결합한 통합 플랫폼으로, 단일 SDK 및 런타임으로 크로스 플랫폼 개발을 지원합니다. .NET 6은 LTS(Long Term Support) 버전으로, 앞으로의 .NET 개발에 중점을 둘 것입니다.
.NET 6 및 이후 버전에서 C#을 사용한 애플리케이션 개발은 다음과 같은 이점과 기능을 제공합니다.
▶ 통합 플랫폼: .NET 6는 단일 플랫폼으로써, 윈도우, 리눅스, macOS 등 다양한 운영 체제에서 실행되는 애플리케이션 개발을 지원합니다. 이로 인해 개발자는 한 번의 학습으로 다양한 플랫폼에 대응할 수 있는 애플리케이션을 만들 수 있습니다.
▶ 성능 향상: .NET 6는 .NET Core의 성능 향상 기능을 계승하면서 추가적인 최적화를 제공하여 높은 성능의 애플리케이션 개발이 가능합니다.
▶ 모듈식 및 레이어드 아키텍처: .NET 6는 모듈식 구조와 레이어드 아키텍처를 지원하여 개발자가 필요한 기능만 선택하여 사용할 수 있습니다. 이를 통해 애플리케이션의 크기를 줄이고, 메모리 사용량을 최소화할 수 있습니다.
▶ 현대적인 개발 도구 및 환경: .NET 6는 최신 개발 도구 및 환경과 호환되어 개발 및 배포 과정을 간소화하고 효율화합니다. 예를 들어, Visual Studio, Visual Studio Code, Rider 등 다양한 IDE에서 C# 개발을 지원합니다.
▶ C# 언어의 발전: .NET 6는 C# 언어의 최신 버전인 C# 10을 지원합니다. 이를 통해 개발자는 최신 C# 언어 기능을 활용하여 코드를 작성할 수 있습니다.
▶ 다양한 애플리케이션 개발 지원: .NET 6를 사용하면 웹 애플리케이션, 데스크톱 애플리케이션, 모바일 애플리케이션, 마이크로서비스, IoT 애플리케이션 등 다양한 유형의 애플리케이션을 개발할 수 있습니다.
.NET 플랫폼의 발전에 따라 C#을 사용한 개발은 계속 발전하고 있으며, 개발자는 이러한 최신 기술과 기능을 활용하여 효율적이고 성능이 뛰어난 애플리케이션을 만들 수 있습니다.
4) 그 외
▶ Xamarin: C#을 사용하여 iOS, Android, 및 Windows 운영 체제를 위한 모바일 애플리케이션을 개발할 수 있는 플랫폼입니다.
▶ Unity: C#을 사용하여 게임 및 인터랙티브 콘텐츠를 개발하는데 사용되는 인기 있는 게임 엔진입니다.
▶ Maui: Microsoft에서 개발한 UI 프레임워크입니다. 이 프레임워크는 XAML 기반의 UI 개발을 위해 사용됩니다. Maui는 .NET Multi-platform App UI의 약자이며, 여러 플랫폼에서 작동하는 애플리케이션 개발을 가능하게 합니다. Maui는 Windows, macOS, iOS, Android 등 다양한 플랫폼에서 작동하는 애플리케이션 개발을 위해 설계되었으며, C# 또는 XAML을 사용하여 개발됩니다. Maui는 .NET 6에서 공식적으로 출시되었으며, Xamarin.Forms를 대체하고 있습니다.
반응형'닷넷 > C#' 카테고리의 다른 글
Microsoft의 Text-to-Speech (TTS), Azure Cognitive Services를 C#에서 사용하는 방법 (0) 2023.07.27 C# Interlocked.Increment에 대한 고찰 (0) 2023.07.20 C# 시간 계산 두 번 째 (TimeSpan, DateTimeOffset) (0) 2023.04.06 C# JSON 파일 읽기/쓰기 (Deserialize/Serialize) (0) 2023.04.04 C# XML 파일 읽기/쓰기 (0) 2023.03.28