기본 문법
String과 StringBuilder 차이
둘 다 참조 타입이며 힙에 할당됩니다. StringBuilder는 기본 용량 16으로 시작하며, 문자 수가 용량을 초과하면 자동 확장됩니다. 단순 문자열 결합에서는 String이 더 효율적일 수 있으며, StringBuilder는 대량 연산 시에만 성능 이점이 있습니다.
확장 메서드
기존 타입에 수정 없이 메서드를 추가하는 기술입니다. 필수 조건:
public static class Extensions {
public static void NewMethod(this string str) { }
}
박싱과 언박싱
박싱은 값 타입을 참조 타입으로 변환하는 암시적 변환이며, 언박싱은 참조 타입을 값 타입으로 변환합니다.
값 타입 vs 참조 타입
값 타입은 변수가 직접 값을 포함하며 복사 시 전체 값이 복제됩니다. 참조 타입은 객체 참조만 복사합니다. 값 타입은 스택에, 참조 타입은 힙에 저장됩니다.
대리자(Delegate)
메서드를 매개변수로 전달하는 메커니즘입니다. Func<TResult>는 반환 값이 있는 반면, Action은 없습니다.
비트 연산 최적화
int result = 2 << 3; // 16 출력
const vs readonly
- const: 선언 시 할당, 컴파일 상수
- readonly: 생성자에서 할당 가능, 런타임 상수
unsafe 모드
포인터 사용을 허용하지만 기본적으로 비활성화됩니다.
객체 지향 프로그래밍
클래스 vs 구조체
클래스는 참조 타입, 상속 지원. 구조체는 값 타입, 상속 불가.
추상 클래스 vs 인터페이스
- 추상 클래스: 부분 구현 가능, 단일 상속
- 인터페이스: 구현 없음, 다중 구현 지원
실행 순서
정적 필드 → 정적 생성자 → 인스턴스 필드 → 생성자
오버라이딩 vs 오버로딩
오버라이딩은 다형성을 위한 메서드 재정의, 오버로딩은 동일 이름의 다양한 매개변수 메서드입니다.
컬렉션과 LINQ
IList vs List
IList는 읽기 전용/고정 크기 컬렉션의 베이스 인터페이스이며, List는 가변 크기 구현체입니다.
LINQ 성능 향상
- 데이터베이스 인덱스 최적화
- Select로 필요한 열만 쿼리
- AsNoTracking()으로 변경 추적 비활성화
지연 실행
IEnumerable 쿼리는 실제 요소 접근 시(예: foreach 내부) 실행됩니다.
제네릭 제약 조건
public void Process<T>() where T : new() { }
다중 스레딩
Task vs Thread
Task는 스레드 풀과 async/await 지원하며, Thread는 저수준 스레드 관리를 제공합니다.
await 동작 원리
비동기 작업 완료 시 콜백을 통해 실행을 재개하며, GetResult()는 현재 스레드를 블로킹합니다.
단일 인스턴스 패턴
public sealed class Singleton {
private static Singleton _uniqueInstance;
private static readonly object _syncRoot = new object();
public static Singleton GetInstance() {
if (_uniqueInstance == null) {
lock (_syncRoot) {
if (_uniqueInstance == null) {
_uniqueInstance = new Singleton();
}
}
}
return _uniqueInstance;
}
}
잠금 메커니즘
lock은 모니터 기반 혼합 잠금이며, 참조 타입 객체에만 적용해야 합니다.
스레드 생명주기
포그라운드 스레드는 프로세스 종료를 막지만, 백그라운드 스레드는 자동 종료됩니다.