C# 내용 정리

알고 있는 중요한 내용 정리

아무래도 여유가 있는 시간에 C# 프로그래밍 언어와 Lua에 대한 기본적인 이해를 정리하는 것이 좋을 것 같습니다. 그래서 알고 있는 C#에 대한 내용을 먼저 정리해보고자 합니다.

C#

C#은 마이크로소프트에서 개발한 객체 지향 프로그래밍 언어로, .NET 프레임워크와 함께 사용됩니다. C#은 Java와 유사한 문법을 가지고 있으며, 강력한 타입 시스템과 메모리 관리 기능을 제공합니다. 주요 특징으로는 다음과 같습니다:

  • 객체 지향: 클래스와 객체를 사용하여 코드를 구조화할 수 있습니다.
  • 타입 시스템: 컴파일 타임에 타입 검사를 수행하여 런타임 오류를 줄일 수 있습니다.
  • 메모리 관리: 가비지 컬렉션을 통해 메모리를 자동으로 관리합니다.
  • LINQ: 데이터 쿼리를 간결하게 작성할 수 있는 기능을 제공합니다.
  • 비동기 프로그래밍: asyncawait 키워드를 사용하여 비동기 작업을 쉽게 처리할 수 있습니다.
  • 플랫폼 독립성: .NET Core를 사용하면 다양한 플랫폼에서 실행할 수 있습니다.

interface와 추상 클래스

C#에서 interface와 추상 클래스는 객체 지향 프로그래밍에서 중요한 개념입니다. 이 둘은 공통된 기능을 정의하는 데 사용되지만, 몇 가지 차이점이 있습니다.

  • interface: 인터페이스는 클래스가 구현해야 하는 메서드, 속성, 이벤트 등을 정의합니다. 인터페이스는 다중 상속을 지원하며, 클래스는 여러 인터페이스를 구현할 수 있습니다. 인터페이스는 구현을 포함하지 않으며, 오로지 계약(Contract)만 정의합니다.
public interface IAnimal
{
    void Speak();
}
  • 추상 클래스: 추상 클래스는 일부 구현을 포함할 수 있는 클래스입니다. 추상 클래스는 다른 클래스가 상속받아 사용할 수 있으며, 추상 메서드를 정의하여 하위 클래스에서 구현하도록 강제할 수 있습니다. 추상 클래스는 다중 상속을 지원하지 않습니다.
public abstract class Animal
{
    public abstract void Speak();
    public void Sleep()
    {
        Console.WriteLine("Sleeping...");
    }
}

더 읽어보기: https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/interfaces

IEnumerable, List, Collection

C#에서 IEnumerable<T>, List<T>, Collection<T>는 컬렉션을 다루는 데 사용되는 중요한 인터페이스와 클래스입니다. 이들은 데이터 구조를 정의하고 데이터를 저장하고 조작하는 데 사용됩니다.

  • IEnumerable: IEnumerable<T>는 컬렉션을 순회할 수 있는 인터페이스입니다. 이 인터페이스를 구현하면 foreach 루프를 사용하여 컬렉션의 요소를 반복할 수 있습니다. IEnumerable<T>는 읽기 전용 컬렉션을 나타내며, 데이터를 수정할 수 없습니다.
public class MyCollection : IEnumerable<int>
{
    private List<int> _items = new List<int>();
 
    public IEnumerator<int> GetEnumerator()
    {
        return _items.GetEnumerator();
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
  • List: List<T>는 가변 크기의 배열을 구현한 클래스입니다. 요소를 추가, 제거, 검색할 수 있으며, 인덱스를 사용하여 요소에 접근할 수 있습니다. List<T>IEnumerable<T>를 구현하므로 foreach 루프를 사용할 수 있습니다.
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
foreach (var number in numbers)
{
    Console.WriteLine(number);
}
  • Collection: Collection<T>IList<T>를 구현하는 클래스입니다. List<T>와 유사하지만, 더 많은 기능을 제공하며, 데이터 바인딩과 같은 시나리오에서 유용합니다. Collection<T>는 기본적으로 변경 알림을 지원하지 않지만, 이를 확장하여 알림 기능을 추가할 수 있습니다.
public class MyCollection : Collection<int>
{
    protected override void InsertItem(int index, int item)
    {
        base.InsertItem(index, item);
        Console.WriteLine($"Item {item} added at index {index}");
    }
 
    protected override void RemoveItem(int index)
    {
        Console.WriteLine($"Item {this[index]} removed from index {index}");
        base.RemoveItem(index);
    }
}

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/csharp/tour-of-csharp/tutorials/list-collection

IEnumerator

IEnumerator는 C#에서 컬렉션을 순회하는 데 사용되는 인터페이스입니다. 이 인터페이스는 컬렉션의 요소를 반복할 수 있는 기능을 제공합니다. IEnumeratorMoveNext(), Reset(), Current 속성을 포함하여 컬렉션의 현재 위치를 추적하고 요소에 접근할 수 있게 합니다.

public class MyEnumerator : IEnumerator<int>
{
    private int[] _items;
    private int _position = -1;
 
    public MyEnumerator(int[] items)
    {
        _items = items;
    }
 
    public bool MoveNext()
    {
        _position++;
        return (_position < _items.Length);
    }
 
    public void Reset()
    {
        _position = -1;
    }
 
    public int Current => _items[_position];
 
    object IEnumerator.Current => Current;
 
    public void Dispose() { }
}

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/api/system.collections.generic.ienumerator-1?view=net-9.0

이벤트와 델리게이트

C#에서 이벤트와 델리게이트는 객체 간의 통신을 위한 중요한 메커니즘입니다. 이 둘은 밀접하게 연결되어 있으며, 이벤트 기반 프로그래밍을 지원합니다.

  • 델리게이트(Delegate): 델리게이트는 메서드 참조를 저장할 수 있는 타입입니다. 특정 시그니처를 가진 메서드를 가리킬 수 있으며, 이를 통해 메서드를 매개변수로 전달하거나 콜백을 구현할 수 있습니다.
public delegate void Notify(string message);
 
public class Notifier
{
    public event Notify OnNotify;
 
    public void Notify(string message)
    {
        OnNotify?.Invoke(message);
    }
}
  • 이벤트(Event): 이벤트는 델리게이트의 특별한 형태로, 객체가 특정 작업을 수행할 때 다른 객체에 알릴 수 있는 메커니즘입니다. 이벤트는 주로 UI 프로그래밍에서 사용자 입력이나 상태 변경을 처리하는 데 사용됩니다. 이벤트는 event 키워드를 사용하여 정의합니다.
public class Button
{
    public event EventHandler Click;
 
    public void OnClick()
    {
        Click?.Invoke(this, EventArgs.Empty);
    }
}

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/csharp/events-overview

메모리 관리

C#의 가비지 컬렉션은 자동 메모리 관리 기능으로, 사용하지 않는 객체를 자동으로 식별하고 메모리를 해제합니다. 이는 개발자가 메모리 누수나 해제되지 않은 객체로 인한 문제를 걱정하지 않아도 되게 합니다. 가비지 컬렉션은 다음과 같은 방식으로 작동합니다:

  1. 세대별 가비지 컬렉션(Genertion-Based): C#은 객체를 세대별로 관리합니다. 새로 생성된 객체는 0세대에 속하고, 사용되지 않으면 1세대로 이동하며, 다시 사용되지 않으면 2세대로 이동합니다. 오래된 객체일수록 가비지 컬렉션이 덜 자주 발생합니다.
  2. 마크 앤 스윕(Mark-and-Sweep): 가비지 컬렉션은 "마크 앤 스윕" 알고리즘을 사용합니다. 먼저, 사용 중인 객체를 마크하고, 이후 마크되지 않은 객체를 메모리에서 해제합니다.
  3. 압축(Compaction): 가비지 컬렉션 후에는 메모리 단편화를 방지하기 위해 객체를 압축할 수 있습니다. 이는 메모리 사용 효율을 높이는 데 도움이 됩니다.

IDisposable과 using

C#에서 IDisposable 인터페이스는 리소스 해제를 위한 메서드를 정의합니다. 이 인터페이스를 구현하면 객체가 더 이상 필요하지 않을 때 리소스를 명시적으로 해제할 수 있습니다. using 문을 사용하면 자동으로 Dispose() 메서드를 호출하여 리소스를 해제할 수 있습니다.

public class Resource : IDisposable
{
    public void Dispose()
    {
        // 리소스 해제 로직
    }
}
using (Resource resource = new Resource())
{
    // 리소스를 사용하는 코드
} // using 블록을 벗어나면 Dispose()가 자동으로 호출됩니다.

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/api/system.idisposable?view=net-9.0

LINQ

LINQ(Language Integrated Query)는 C#에서 데이터 쿼리를 간결하게 작성할 수 있는 기능입니다. LINQ를 사용하면 컬렉션, 데이터베이스, XML 등 다양한 데이터 소스에 대해 일관된 방식으로 쿼리를 작성할 수 있습니다. 주요 특징은 다음과 같습니다:

  • 쿼리 구문: SQL과 유사한 구문을 사용하여 데이터를 필터링, 정렬, 그룹화할 수 있습니다.
  • 메서드 구문: 메서드 체이닝을 사용하여 쿼리를 작성할 수 있습니다. Where, Select, OrderBy 등의 메서드를 사용합니다.
  • 지연 실행: LINQ 쿼리는 실제로 데이터를 요청할 때까지 실행되지 않습니다. 이는 성능을 최적화하는 데 도움이 됩니다.
  • 확장 메서드: LINQ는 IEnumerable<T> 인터페이스에 확장 메서드를 추가하여 컬렉션에 대한 쿼리를 쉽게 작성할 수 있습니다.

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/csharp/tutorials/working-with-linq

비동기 프로그래밍

C#의 비동기 프로그래밍은 asyncawait 키워드를 사용하여 구현됩니다. 이를 통해 I/O 작업이나 네트워크 요청과 같은 시간이 오래 걸리는 작업을 비동기적으로 처리할 수 있습니다. 이는 JavaScript와도 비슷하며, 주요 특징은 다음과 같습니다:

  • 비동기 메서드: async 키워드를 사용하여 비동기 메서드를 정의할 수 있습니다. 이 메서드는 Task 또는 Task<T>를 반환합니다.
  • 대기: await 키워드를 사용하여 비동기 작업이 완료될 때까지 기다릴 수 있습니다. 이때, UI 스레드가 블로킹되지 않아 사용자 인터페이스가 응답성을 유지합니다.
  • 예외 처리: 비동기 메서드 내에서 발생한 예외는 await를 통해 처리할 수 있습니다. 이는 일반적인 동기 메서드와 유사하게 작동합니다.

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/csharp/asynchronous-programming/async-scenarios

제네릭 (Generics)

C#의 제네릭은 클래스, 인터페이스, 메서드 등을 특정 타입에 국한되지 않고 일반화된 방식으로 정의할 수 있게 해줍니다. 이를 통해 코드 재사용성을 높이고 타입 안정성을 보장할 수 있습니다. 주요 특징은 다음과 같습니다:

  • 타입 매개변수: <T>와 같이 타입 매개변수를 사용하여 일반화된 코드를 작성합니다. 실제 사용 시 구체적인 타입으로 대체됩니다.
  • 타입 안정성: 컴파일 시점에 타입 검사를 수행하여 런타임에 발생할 수 있는 타입 오류를 줄입니다.
  • 성능: 값 형식에 대해 박싱(boxing) 및 언박싱(unboxing) 오버헤드를 줄여 성능을 향상시킬 수 있습니다. 컬렉션 클래스에서 특히 유용합니다.
public class GenericRepository<T> where T : class
{
    protected readonly List<T> _items = new List<T>();
 
    public void Add(T item)
    {
        _items.Add(item);
    }
 
    public T GetById(int id)
    {
        return _items.FirstOrDefault();
    }
}

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/types/generics

예외 처리 (Exception Handling)

C#에서 예외 처리는 프로그램 실행 중 발생할 수 있는 오류나 예외적인 상황을 관리하는 메커니즘입니다. try-catch-finally 블록을 사용하여 예외를 처리합니다.

  • try: 예외가 발생할 가능성이 있는 코드를 포함합니다.
  • catch: try 블록에서 특정 예외가 발생했을 때 실행될 코드를 정의합니다. 다양한 타입의 예외를 처리하기 위해 여러 catch 블록을 사용할 수 있습니다.
  • finally: 예외 발생 여부와 관계없이 항상 실행되어야 하는 코드를 포함합니다. 주로 리소스 해제 등에 사용됩니다.
  • throw: 예외를 명시적으로 발생시킬 때 사용합니다.
public class FileManager
{
    public static string ReadFile(string filePath)
    {
        try
        {
            string content = System.IO.File.ReadAllText(filePath);
            return content;
        }
        catch (System.IO.FileNotFoundException ex)
        {
            Console.WriteLine($"파일을 찾을 수 없습니다: {ex.FileName}");
            throw;
        }
        catch (System.Exception ex)
        {
            Console.WriteLine($"오류 발생: {ex.Message}");
            return string.Empty;
        }
        finally
        {
            Console.WriteLine("파일 읽기 시도 완료.");
        }
    }
}

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/csharp/fundamentals/exceptions/exception-handling

속성 (Properties)

속성은 필드처럼 보이지만 접근자(getter)와 설정자(setter) 메서드를 통해 값을 읽거나 쓰는 클래스 멤버입니다. 캡슐화를 유지하면서 데이터에 유연하게 접근할 수 있도록 합니다.

  • 캡슐화: 내부 구현을 숨기고 데이터 접근을 제어할 수 있습니다.
  • 유효성 검사: 설정자(setter)에서 값의 유효성을 검사하는 로직을 추가할 수 있습니다.
  • 계산된 값: getter에서 다른 필드 값을 기반으로 계산된 값을 반환할 수 있습니다.
  • 자동 구현 속성: 간단한 속성의 경우 컴파일러가 자동으로 백업 필드와 기본 접근자를 생성합니다. public string Name { get; set; }
public class Person
{
    private string _name;
 
    public string Name
    {
        get { return _name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentException("이름은 null이거나 공백일 수 없습니다.");
            }
            _name = value;
        }
    }
    public int Age { get; set; }
    public string Details => $"{Name} (Age: {Age})";
}

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/properties

람다 식 (Lambda Expressions)

람다 식은 익명 함수를 간결하게 표현하는 방법입니다. 주로 LINQ 쿼리나 델리게이트를 사용하는 곳에서 코드를 짧고 가독성 있게 만드는 데 사용됩니다.

  • 구문: (매개변수) => 표현식 또는 문장 블록 형태로 작성됩니다.
  • 간결성: 메서드를 별도로 정의하지 않고 인라인으로 함수 로직을 작성할 수 있습니다.
  • 활용: LINQ의 메서드 기반 쿼리, 이벤트 핸들러, Task 생성 등 다양한 곳에서 활용됩니다.
public class LambdaExample
{
    public void Demonstrate()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
 
        List<int> evenNumbers = numbers.Where(n => n % 2 == 0).ToList();
        // evenNumbers: [2, 4, 6]
 
        List<int> squaredNumbers = numbers.Select(n => n * n).ToList();
        // squaredNumbers: [1, 4, 9, 16, 25, 36]
 
        Action<string> printMessage = message => Console.WriteLine(message);
        printMessage("Hello Lambda!");
 
        Func<int, int, int> add = (a, b) => a + b;
        int sum = add(5, 3); // sum: 8
    }
}

더 읽어보기: https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/operators/lambda-expressions

Unity와 C#

Unity는 C#을 주요 스크립트 언어로 사용하여 게임 개발을 지원합니다. Unity에서 C#을 사용하면 다음과 같은 기능을 활용할 수 있습니다:

  • 컴포넌트 기반 아키텍처: Unity는 게임 오브젝트에 컴포넌트를 추가하여 기능을 확장할 수 있는 구조를 제공합니다. C# 스크립트는 이러한 컴포넌트를 구현하는 데 사용됩니다.
  • MonoBehaviour 클래스: Unity의 C# 스크립트는 MonoBehaviour 클래스를 상속받아 작성됩니다. 이를 통해 Unity의 생명주기 메서드(예: Start, Update)를 오버라이드하여 게임 로직을 구현할 수 있습니다.
  • 이벤트 시스템: Unity는 C# 이벤트와 델리게이트를 사용하여 게임 오브젝트 간의 상호작용을 처리할 수 있습니다. 이를 통해 게임 내에서 발생하는 다양한 이벤트를 쉽게 관리할 수 있습니다.

더 읽어보기: https://docs.unity.com/