C# 반사 이해 및 실무 활용

1. C# 반사란 무엇인가?

**반사는 .NET에서 실행 중인 응용 프로그램의 어셈블리, 모듈, 타입, 방법, 속성 등의 메타데이터 정보를 가져올 수 있는 강력한 기능입니다.** 이 기능을 통해 개발자는 실행 중인 프로그램의 내부 구조를 분석하고 동적으로 조작할 수 있습니다.

.NET 응용 프로그램은 다음의 주요 컴포넌트로 구성됩니다:

  • 어셈블리 (Assembly)
  • 모듈 (Module)
  • 타입 (Type)

반사 기능은 System.Reflection 네임스페이스를 통해 제공되며, 다음의 주요 클래스들을 통해 사용할 수 있습니다:

  • Assembly: 어셈블리 정보 가져오기 및 로딩
  • Type: 타입 정보 (메서드, 생성자, 속성 등) 가져오기
  • MethodInfo: 메서드 정보 및 실행
  • 필드, 이벤트, 인터페이스 정보 가져오기 위한 클래스들

2. 어셈블리와 네임스페이스의 관계

네임스페이스는 개발자가 정의한 이름공간으로, 전역 엔티티들을 분리하여 관리합니다. 반면 어셈블리는 .NET 실행 파일 (.dll, .exe)의 단위로, 이들은 서로 일대일 대응하지 않습니다. 하나의 어셈블리에 여러 네임스페이스가 포함될 수 있고, 하나의 네임스페이스가 여러 어셈블리에 걸쳐있을 수 있습니다.

3. 반사의 활용: 동적 플러그인 관리 시스템

플러그인 관리 시스템을 통해 응용 프로그램의 기능을 동적으로 확장할 수 있습니다. 예를 들어 미디어 플레이어가 다양한 파일 형식을 지원하도록 하려면:

` public interface ICodec { string Extension { get; } IDecoder GetDecoder(); } `

모든 플러그인은 이 인터페이스를 구현해야 합니다. 플러그인 관리자는 다음과 같이 작동합니다:

1. 특정 디렉터리 내의 DLL 파일들을 검색합니다. 2. 각 DLL 파일에 대한 어셈블리 정보를 가져옵니다. 3. 인터페이스를 구현한 타입들을 검색합니다. 4. 발견된 타입들로 인스턴스를 생성하고 실행 중인 응용 프로그램에 등록합니다.

4. 반사 기반 플러그인 관리 코드示例

` using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Loader; public class PluginManager { private readonly string _pluginPath; private FileSystemWatcher _fileSystemWatcher; private List _plugins = new List(); public event Action PluginLoaded; public event Action PluginUnloaded; public PluginManager(string pluginPath) { _pluginPath = pluginPath; _fileSystemWatcher = new FileSystemWatcher(pluginPath, "*.dll"); _fileSystemWatcher.IncludeSubdirectories = true; _fileSystemWatcher.Created += OnFileCreated; _fileSystemWatcher.Deleted += OnFileDeleted; _fileSystemWatcher.EnableRaisingEvents = true; } public void LoadPlugins() { if (!Directory.Exists(_pluginPath)) return; _plugins.Clear(); var files = Directory.GetFiles(_pluginPath, "*.dll", SearchOption.AllDirectories); foreach (var file in files) { try { var assembly = Assembly.LoadFrom(file); var pluginTypes = assembly.GetTypes() .Where(t => typeof(IFeaturePlugin).IsAssignableFrom(t)); foreach (var type in pluginTypes) { if (!type.IsAbstract && !type.IsInterface) { var plugin = (IFeaturePlugin)Activator.CreateInstance(type); plugin.Load(); _plugins.Add(plugin); PluginLoaded?.Invoke(); } } } catch (Exception ex) { Console.WriteLine($"로딩 중 오류 발생: {ex.Message}"); } } } private void OnFileCreated(object sender, FileSystemEventArgs e) { LoadPlugins(); } private void OnFileDeleted(object sender, FileSystemEventArgs e) { UnloadPlugin(e.FullPath); } public void UnloadPlugin(string pluginPath) { var assembly = AppDomain.CurrentDomain.GetAssemblies() .FirstOrDefault(a => a.Location == pluginPath); if (assembly == null) return; var pluginTypes = assembly.GetTypes() .Where(t => typeof(IFeaturePlugin).IsAssignableFrom(t)); foreach (var type in pluginTypes) { var plugin = _plugins.FirstOrDefault(p => p.GetType() == type); if (plugin != null) { plugin.Dispose(); _plugins.Remove(plugin); PluginUnloaded?.Invoke(); } } } ~PluginManager() { _fileSystemWatcher.Dispose(); } } public interface IFeaturePlugin : IDisposable { Guid Id { get; } string Name { get; } void Execute(); void Load(); void Dispose(); } `

5. 플러그인 개발 및 사용 방법

플러그인 개발자는 다음을 준수해야 합니다:

  • IFeaturePlugin 인터페이스를 구현
  • 고유 식별자 제공
  • 플러그인 로드 및 언로드 시에 필요한 작업 수행
` public class ImageDecoder : IFeaturePlugin { public Guid Id => new Guid("12345678-1234-1234-1234-1234567890AB"); public string Name => "PNG 이미지 디코더"; public void Execute() { // 디코딩 로직 구현 } public void Load() { // 플러그인 로드 시 초기화 작업 } public void Dispose() { // 리소스 해제 } } `

태그: C# reflection PluginSystem DynamicLoading

6월 27일 18:08에 게시됨