C# 치트시트 — 빠른 레퍼런스
C# 12(.NET 8 포함) 문법, OOP, LINQ 및 가장 일반적으로 사용되는 표준 라이브러리 치트시트로, 일상적인 시나리오의 약 80%를 다룹니다.
C# C# 12 (with .NET 8)
.NET (Core / 5+ / 8) · OOP, 제네릭, 함수형, 동시성 · 정적, 강타입, 공칭 타입
추천 학습 경로
먼저 「Hello World 및 빌드 환경」(dotnet new console)을 실행 → 변수, 타입, 제어 흐름에 익숙해지기 → 컬렉션과 LINQ로 데이터 처리 → 객체 지향(class/interface) 이해 → async/await 비동기 프로그래밍 숙달 → 마지막으로 필요할 때 파일, 네트워크, 정규식, 빌드/디버그 참조. FAQ 섹션은 돌아와서 함정을 피하는 데 좋습니다.
1.Hello World 및 빌드 환경
.NET 프로그램을 처음부터 만들고 실행하고 구성합니다: 최상위 문, 프로젝트 파일, 네임스페이스, 명령줄 인자.
최소 프로그램
C# 9+는 최상위 문을 지원합니다. Program.cs에 직접 실행 코드를 작성할 수 있으며 class와 Main 보일러플레이트가 필요 없습니다. Console.WriteLine이 한 줄을 출력합니다.
생성과 실행
dotnet CLI는 .NET의 명령줄 진입점입니다. dotnet new로 프로젝트를 만들고, dotnet run으로 컴파일 후 실행하며, dotnet build는 컴파일만 합니다.
명령줄 인자
최상위 문에서 args는 string[] 명령줄 인자이며 args[0]부터 사용자 인자입니다. 전통적인 Main(string[] args)와 동등합니다.
종료 코드
최상위 문은 return으로 종료 코드를 지정할 수 있으며 0은 성공, 0이 아닌 값은 오류 범주를 나타냅니다. Environment.ExitCode도 읽기/쓰기가 가능합니다.
네임스페이스
namespace는 타입을 조직하여 이름 충돌을 피합니다. 파일 범위 네임스페이스(C# 10)는 세미콜론으로 선언하여 중괄호와 들여쓰기를 생략합니다.
using 지시문
using 지시문은 네임스페이스를 가져와 완전한 한정 이름이 필요 없게 합니다. GlobalUsings.cs의 전역 using은 프로젝트 전체에서 가져오기를 공유합니다.
최상위 문 세부 사항
최상위 문은 Program 클래스의 Main 메서드로 컴파일됩니다. 프로젝트당 하나만 가능하며, 보조 메서드가 필요하면 로컬 함수를 사용합니다.
csproj 프로젝트 파일
csproj는 프로젝트 구성입니다. TargetFramework 대상 프레임워크, ImplicitUsings 암시적 using, Nullable null 허용 컨텍스트.
2.변수와 상수
타입 추론, const/readonly, nullable 타입, null 병합, nameof, 스코프.
var 타입 추론
var는 컴파일러가 초기화 식에서 타입을 추론하게 합니다. 로컬 변수에는 사용할 수 있지만 필드/속성에는 사용할 수 없습니다. 초기화 식의 타입이 분명할 때는 var를 우선 사용하면 간결합니다.
const와 readonly
const는 컴파일 타임 상수(기본 타입)이고, readonly는 런타임 읽기 전용 필드(생성자에서 할당 가능)입니다. 정적 상수에는 보통 static readonly를 사용합니다.
Nullable 타입
Nullable<T>(int? 등)는 값 타입에 null을 허용하며, null 허용 참조 타입(? 접미사 + Nullable enable)은 컴파일러가 null 참조를 정적으로 분석하게 합니다.
Null 병합과 Null 조건
??는 왼쪽이 null이면 오른쪽을 반환하고, ?.는 왼쪽이 null이면 null로 단락(short-circuit)합니다. 연쇄 ?.로 깊은 접근을 null 안전하게 작성할 수 있습니다.
nameof 식
nameof은 심볼 이름을 문자열로 반환하며 이름 변경 시 자동으로 동기화됩니다. 매개변수 검증, 속성 알림, 로그 태그에 자주 사용됩니다.
스코프
C#는 중괄호로 블록 스코프를 정의합니다. 로컬 변수는 필드와 같은 이름을 가질 수 있지만 가리기는 피하세요. using 선언은 리소스 스코프를 블록 끝까지 제한합니다.
init과 속성 초기화
init 접근자는 객체 초기화 식에서 할당을 허용하고 그 후에는 읽기 전용입니다. 속성 초기화 식은 기본값을 제공하며, set 접근자는 검증을 추가할 수 있습니다.
구조 분해
튜플과 record는 구조 분해를 지원합니다. 괄호로 요소를 여러 변수로 분해합니다. Deconstruct 메서드로 구조 분해 로직을 사용자 정의할 수 있습니다.
3.데이터 타입
내장 타입, nullable, 열거형, 구조체, 제네릭, record, 튜플, 타입 변환.
내장 타입
int/long/double/bool 등의 내장 타입은 System 타입으로 매핑됩니다. var는 단지 추론이며 런타임 타입은 변하지 않습니다.
Nullable 타입
int?는 nullable 값 타입(Nullable<int>)이며 HasValue/Value로 접근합니다. Nullable enable이 활성화되면 nullable 참조 타입이 컴파일러에 의해 정적으로 검사됩니다.
열거형
enum은 명명된 정수 상수를 정의하며 기본적으로 int입니다. Flags 특성을 사용하면 비트 조합을 HasFlag이나 비트 연산으로 판정할 수 있습니다.
구조체
struct는 값 타입입니다. 할당 시 전체가 복사되고, 스택에 할당되며, null일 수 없습니다(null 허용하면 박싱됨). 작고 불변인 데이터에 적합합니다.
클래스(참조 타입)
class는 참조 타입입니다. 할당 시 같은 객체를 공유하고, GC가 메모리를 관리하며, null일 수 있습니다. 가변 상태에는 class를 사용합니다.
제네릭
제네릭은 타입을 매개변수화하여 컴파일 타임에 강타입 코드를 생성하므로 런타임 박싱이 없습니다. T는 타입 매개변수이며, where 절로 제약 조건을 추가합니다.
record 타입
record(C# 9)는 값 의미론을 가진 참조 타입입니다. 값 동등성, ToString, 구조 분해, with 식이 내장되어 있습니다. DTO와 불변 데이터에 적합합니다.
튜플
튜플은 여러 값을 묶고 명명된 요소를 지원합니다. 여러 값을 반환하거나 빠르게 데이터를 집계할 때 가장 편리하며, 임시에는 튜플, 장기에는 record를 사용합니다.
타입 변환
is는 안전한 타입 검사, as는 안전한 변환(실패 시 null), (T)는 강제 변환(실패 시 예외), Convert/Parse는 명시적 파싱입니다.
패턴 매칭
is / switch는 패턴 매칭을 사용합니다. 타입 패턴, 속성 패턴, 위치 패턴. 많은 if + cast를 대체하며 코드가 의도를 더 명확히 표현합니다.
4.참조와 배열
값 타입과 참조 타입, 배열, Span, 인덱스/범위, ref/in/out, unsafe 포인터.
값 타입 vs 참조 타입
값 타입(struct / 열거형 / 기본 타입)은 할당 시 복사되고, 참조 타입(class / 인터페이스)은 같은 객체를 공유합니다. 이것이 C#에서 가장 중요한 멘탈 모델입니다.
배열
배열은 고정 길이 참조 타입이며 []로 인덱스 접근합니다. 다차원과 가변 배열은 다릅니다: [,]는 직사각형, [][]는 지그재그. 배열은 Count가 아닌 Length를 사용합니다.
인덱스와 범위
^는 끝에서부터 인덱싱(^1은 마지막), ..은 범위(1..^1은 처음과 끝 제외)를 나타냅니다. 배열/List의 부분 집합을 자르는 모던한 표기법입니다.
Span과 메모리
Span<T>는 임의의 연속 메모리에 대한 읽기 전용 뷰이며 할당 없이 슬라이스할 수 있어 고성능 데이터 처리의 핵심입니다. 스택이나 힙 메모리를 가리킬 수 있습니다.
ref / in / out 매개변수
ref는 읽기/쓰기 가능한 참조 전달, in은 읽기 전용 참조 전달, out은 반환값용(호출 전 초기화 불필요)입니다. 참조 전달로 값 타입 복사를 피합니다.
unsafe 포인터
unsafe 컨텍스트에서는 실제 포인터(int* 등)를 사용할 수 있습니다. csproj에서 AllowUnsafeBlocks를 활성화해야 합니다. 네이티브 상호 운용 시에만 사용하고 일반 코드에서는 피하세요.
Memory와 버퍼
Memory<T>는 Span의 힙 안전 버전으로 필드에 저장하고 async를 가로질러 사용할 수 있습니다. ReadOnlyMemory는 읽기 전용 뷰입니다. 비동기 버퍼 데이터 처리에 사용됩니다.
복사 의미론
값 타입은 기본적으로 값 전달(큰 struct는 복사 비용이 발생), 객체(참조)는 참조 전달입니다. 객체를 제자리에서 수정해야 한다면 ref를 사용하지 않습니다.
5.제어 흐름
if/else, for/foreach/while, switch 식, break/continue, throw 식.
if / else
if / else는 조건에 따라 분기합니다. C#는 비교에 ==, 단락(short-circuit)에 &&와 ||를 사용합니다. 단일 문은 중괄호를 생략할 수 있지만 유지하는 것을 권장합니다.
for 루프
클래식 for: 초기화, 조건, 증감. 인덱스가 필요할 때 사용하고, 대부분의 경우 foreach가 더 안전합니다.
foreach 반복
foreach는 인덱스 없이 컬렉션을 반복하며 컴파일 타임에 안전합니다. var와 LINQ와 함께 체인으로 데이터를 처리하는 것이 가장 일반적입니다.
while / do-while
while은 먼저 판단 후 실행하고, do-while은 최소 한 번은 실행합니다. EOF까지 스트림 읽기 등 루프 횟수를 모르는 시나리오에 적합합니다.
switch 식
switch 식(C# 8)은 =>로 값을 반환하며 긴 if-else 체인을 대체합니다. 타입 패턴과 when 가드의 조합은 강력합니다.
break와 continue
continue는 현재 반복을 건너뛰고 다음으로, break는 루프를 빠져나가고, return은 메서드를 빠져나갑니다. 다중 루프 탈출에는 goto를 사용합니다.
삼항 연산자
cond ? a : b로 if / else 할당을 한 줄로 표현합니다. 분기 표현식 타입은 호환되어야 합니다(공통 타입으로 변환 가능).
throw 식
throw는 식으로 ??와 ?:의 오른쪽, 그리고 메서드 본문을 한 줄로 쓰는 데 사용할 수 있습니다. ArgumentNullException을 던져 매개변수 검증을 간결하게 작성합니다.
6.함수와 메서드
메서드 시그니처, 매개변수 전달, 오버로드, 로컬 함수, 람다, 식 본문 멤버.
메서드 정의
메서드 = 반환 타입 + 이름 + 매개변수 목록 + 본문. void는 반환값이 없음을 나타냅니다. 접근 한정자가 가시성을 제어합니다.
매개변수 전달
기본적으로 값 전달입니다. 메서드 내 수정은 호출자 변수에 영향을 주지 않습니다. 객체는 참조 복사본이 전달되므로 멤버를 수정하면 원본 객체에 영향을 줍니다.
선택적 매개변수와 명명된 매개변수
선택적 매개변수는 기본값을 가지며(매개변수 목록 끝에 위치해야 함), 명명된 매개변수로 이름을 기반으로 전달하여 중간 선택적 매개변수를 건너뛸 수 있습니다.
ref / out / in
ref는 읽기/쓰기 참조 전달, out은 출력 전용(호출 전 초기화 불필요), in은 읽기 전용 참조 전달(큰 값 타입의 복사 회피).
메서드 오버로드
오버로드 = 같은 이름의 메서드이지만 매개변수 목록이 다름(개수/타입). 컴파일러가 인자에 따라 가장 일치하는 버전을 선택합니다. 반환 타입은 오버로드 결정에 참여하지 않습니다.
식 본문 멤버
메서드/속성/생성자가 단일 식이면 =>로 간결하게 작성할 수 있습니다. 람다와 비슷하지만, 식 본문 멤버는 진짜 멤버입니다.
로컬 함수
메서드 안에 정의된 함수는 외부 변수(클로저)에 접근할 수 있으며, 재귀 보조 함수나 반복기 내부 로직에 자주 사용됩니다.
람다 식
익명 함수: 매개변수 => 식. 대리자/Func/Action과 함께 사용되며 LINQ의 핵심 구문입니다.
대리자
대리자는 메서드 타입입니다. 시그니처를 선언하고, +=로 여러 메서드를 구독하며, 대리자를 호출하면 순서대로 발화합니다. 이벤트는 대리자를 기반으로 합니다.
7.문자열
문자열 리터럴, 보간, 일반 메서드, 가변 StringBuilder, 포맷팅.
문자열 리터럴
이중 따옴표 일반 문자열, @ 축자 문자열(이스케이프 처리 안 함), $ 보간, $$ 복합 리터럴(C# 11).
문자열 보간
$ 접두사는 {} 안의 식 결과를 문자열에 인라인합니다. 포맷과 정렬도 지정할 수 있어 + 연결보다 가독성이 좋습니다.
연결과 비교
+ 연결, string.Concat 일괄 연결, string.Join 구분자 연결. 문자열 동등 비교는 ==(값 비교)로 합니다.
일반 메서드
Length, Substring, Contains, StartsWith, IndexOf, Replace, Trim, Split, ToUpper/ToLower는 대부분의 문자열 처리를 포괄합니다.
StringBuilder
대량의 연결에는 StringBuilder를 사용하여 문자열을 반복 생성하지 않습니다(문자열은 불변이며 +는 매번 새 객체를 생성). 루프 내 연결에서 특히 두드러집니다.
문자 char
char는 UTF-16 단일 문자입니다. char.IsDigit/IsLetter/IsWhiteSpace로 분류를 판정합니다. string은 불변이고 char는 반복 가능합니다.
포맷팅
string.Format / $ 보간은 포맷 지정자를 사용합니다: D 정수, F 소수, C 통화, P 백분율, X 16진수.
문자열 파싱
Parse는 문자열을 숫자로 변환하며(실패 시 예외), TryParse는 안전합니다(bool과 out 결과를 반환). TryParse를 우선합니다.
8.컬렉션과 LINQ
List/Dictionary/HashSet 등 일반 컬렉션, IEnumerable, LINQ 체인 쿼리.
List 동적 배열
List<T>는 가변 길이 배열로 Add/Insert/Remove/IndexOf가 있으며, 인덱스 접근은 O(1)입니다. foreach로 반복합니다.
Dictionary 사전
Dictionary<K,V>는 키-값 매핑으로 키 기준 O(1) 조회. TryGetValue로 안전하게 가져오고, KeyValuePair를 반복합니다.
HashSet 집합
HashSet<T>는 중복이 없고 O(1)의 포함 판정. UnionWith/IntersectWith/ExceptWith로 집합 연산을 수행하여 중복 제거 및 병합합니다.
Queue와 Stack
Queue<T>는 FIFO(Enqueue/Dequeue), Stack<T>는 LIFO(Push/Pop). 작업 큐나 실행 취소 스택 처리에 사용합니다.
IEnumerable과 지연
IEnumerable<T>는 읽기 전용 시퀀스 인터페이스이며, LINQ는 이를 기반으로 지연 평가를 수행합니다(반복 시 계산). 배열/List/Dictionary가 모두 구현합니다.
LINQ 필터링과 투영
Where는 필터링, Select는 투영(매핑), OrderBy는 정렬, Distinct는 중복 제거. 메서드 체인은 가독성이 좋고 지연됩니다.
LINQ 집계
Count/Sum/Average/Min/Max로 집계하고, Any/All로 판정하며, First/Single로 요소를 가져옵니다(일치 없으면 예외, FirstOrDefault는 기본값 반환).
GroupBy 그룹화
GroupBy는 키로 그룹화하여 IGrouping 시퀀스를 생성합니다. 통계(범주별 카운트, 합계)에 자주 사용됩니다.
LINQ 쿼리 구문
from/where/orderby/select 쿼리 구문은 메서드 체인의 선언적 작성법이며 컴파일 후 동등합니다. 가독성 선호의 문제입니다.
9.메모리와 리소스 관리
GC 자동 회수, IDisposable과 using으로 비관리 리소스 해제, 약한 참조, 객체 풀.
GC 가비지 컬렉션
C# 메모리는 GC가 자동으로 관리합니다. 힙의 객체가 더 이상 참조되지 않으면 회수됩니다. 세대별 회수(0/1/2)가 성능을 최적화합니다.
IDisposable 인터페이스
비관리 리소스(파일/네트워크/DB 연결)를 보유한 클래스는 IDisposable을 구현하고 Dispose에서 리소스를 해제합니다.
using 문과 선언
using은 스코프 종료 시 Dispose를 보장합니다(finally 의미론). using 선언(C# 8)은 블록 종료 시 리소스를 자동 해제합니다.
종료자
소멸자 ~Class()는 종료자입니다. GC 회수 전에 호출되지만 시점을 보장할 수 없습니다. 비관리 리소스 해제에만 사용하고, 일반적으로는 Dispose 패턴을 따릅니다.
약한 참조
WeakReference는 GC의 대상 회수를 막지 않으며, 캐시(무거운 객체의 사전 캐시, 회수 허용)에 사용됩니다. Target은 언제든지 null이 될 수 있습니다.
객체 풀과 ArrayPool
큰 배열을 자주 할당할 때는 ArrayPool로 버퍼를 재사용하여 GC 압력을 줄입니다. ArrayPool<T>.Shared.Rent와 Return을 쌍으로 사용합니다.
stackalloc 스택 할당
stackalloc은 스택에 메모리를 할당하며 빠르고 GC를 발생시키지 않습니다. 작은 임시 버퍼에 적합하지만 스택 공간은 제한적이므로 주의하세요.
GC 메모리 압력
대량의 네이티브 메모리를 할당할 때는 GC.AddMemoryPressure로 GC에 알려 회수기가 관리 객체를 즉시 회수하게 하여 메모리 사용량 통제를 막습니다.
10.객체 지향
클래스, 속성, 생성자, 상속, 다형성, 인터페이스, 추상 클래스, 접근 한정자.
클래스와 객체
class는 데이터 타입(참조 타입)을 정의합니다. 필드는 상태를 저장하고, 속성은 접근을 제어하며, 메서드는 동작을 정의하고, 생성자는 초기화합니다.
속성
속성은 필드의 안전한 접근자입니다. get은 읽기, set은 쓰기이며 접근 한정자와 검증 로직을 추가할 수 있습니다. 컴파일러가 백킹 필드를 생성합니다.
상속
C#는 단일 상속입니다. class는 :로 하나의 기본 클래스를 상속합니다. 파생 클래스는 기본 클래스의 is-a입니다. private 멤버는 상속되지 않고, protected는 접근 가능합니다.
다형성
virtual과 override로 다형성을 구현합니다. 기본 클래스 참조로 호출된 메서드는 실제 타입에 따라 디스패치됩니다. 메서드는 virtual이어야만 재정의될 수 있습니다.
추상 클래스
abstract 클래스는 인스턴스화할 수 없으며 abstract 메서드(파생 클래스가 반드시 구현)를 포함할 수 있습니다. 추상 메서드는 본문이 없습니다. 템플릿 기본 클래스에 사용됩니다.
인터페이스
interface는 계약을 정의합니다. 멤버는 구현이 없으며 구현 클래스가 모두 제공합니다. C#는 다중 인터페이스 구현을 지원합니다(다중 상속의 대안).
접근 한정자
public은 공개, private은 비공개, protected는 파생 클래스에 표시, internal은 동일 어셈블리 내 표시. 클래스 기본값은 private, 멤버 기본값은 private.
sealed와 object 메서드
sealed 클래스는 상속할 수 없으며, sealed override 메서드는 더 이상 재정의할 수 없습니다. 모든 클래스는 암묵적으로 object(ToString/Equals/GetHashCode)를 상속합니다.
정적 클래스와 확장 메서드
static class는 정적 멤버만 포함할 수 있으며 인스턴스화할 수 없습니다. 확장 메서드는 정적 클래스 안의 정적 메서드로, this 매개변수로 인스턴스에서 호출할 수 있습니다.
11.예외 처리
try/catch/finally, 예외 타입, 사용자 정의 예외, 예외의 비용, 모범 사례.
try / catch / finally
try는 오류가 발생할 수 있는 코드를 담고, catch는 잡아 처리하며, finally는 성공/실패와 관계없이 실행됩니다(리소스 정리). 예외는 상위로 전파됩니다.
다중 catch와 예외 필터
여러 catch는 타입으로 매치되며 구체적인 예외가 먼저 옵니다. when으로 필터 조건을 지정합니다. 변수를 선언하지 않는 catch는 예외 객체를 무시합니다.
예외 던지기
throw new는 예외를 던지고, throw;는 인자 없이 그대로 재던지며(스택 유지), catch에서 new 후 throw는 스택을 재설정합니다.
사용자 정의 예외
사용자 정의 예외는 Exception을 상속하며(관례상 접미사 Exception 사용), 생성자를 제공하며, 내부 예외를 InnerException으로 보존합니다.
finally 정리
finally는 try의 예외 발생 여부와 관계없이 리소스 해제/상태 복원을 보장합니다. return도 finally를 먼저 실행합니다. using은 그것의 구문 설탕입니다.
예외의 비용
예외 포획은 비용이 크므로 제어 흐름으로 사용하면 안 됩니다. 예측 가능한 오류는 반환 코드/TryXxx/Result 패턴으로 처리합니다.
전역 예외 처리
최상위에서 try/catch로 미처리 예외를 잡고 로그를 남깁니다. ASP.NET에서는 미들웨어(UseExceptionHandler)로 일괄 처리합니다.
InnerException 체인
예외를 잡은 후 새 예외를 던지고 원본 예외를 InnerException으로 전달하여 전체 오류 체인을 보존하면, 로그 분석에 더 유용합니다.
12.파일과 I/O
File/Directory/Path 정적 유틸리티, Stream 읽기/쓰기, 비동기 I/O, 바이너리 처리.
텍스트 파일 읽기/쓰기
File.ReadAllText/WriteAllText는 작은 파일을 한 번에 읽기/쓰기하며, ReadAllLines는 줄 단위 배열을 반환합니다. 큰 파일에는 StreamReader를 사용합니다.
StreamReader 줄 단위
큰 파일은 줄 단위로 스트리밍 읽기하여 메모리를 차지하지 않습니다. StreamReader.ReadLine은 null(파일 끝)까지 루프합니다.
디렉터리 작업
Directory는 디렉터리 생성/삭제/열거, Directory.GetFiles/EnumerateFiles는 파일 검색. 재귀 열거에는 SearchOption을 사용합니다.
Path 경로 처리
Path는 크로스 플랫폼 경로 결합/파싱을 합니다: Combine, GetExtension, GetFileName, ChangeExtension. 경로 구분 기호를 손으로 작성하지 마세요.
비동기 I/O
async/await I/O는 스레드를 차단하지 않습니다. ReadAllTextAsync/WriteAllTextAsync/Stream 메서드. UI/서버 동시성 시나리오에서 필수입니다.
바이너리 읽기/쓰기
BinaryWriter/BinaryReader는 타입별로 읽기/쓰고, MemoryStream은 메모리 내 스트림입니다. 네트워크와 파일의 바이너리 프로토콜에서 흔히 사용됩니다.
콘솔 I/O
Console.ReadLine은 한 줄을, ReadKey는 키 입력을, WriteLine은 출력을 담당합니다. Console.In/Out 스트림을 리디렉션하여 테스트할 수 있습니다.
파일 정보
FileInfo/DirectoryInfo는 파일 메타데이터와 메서드를 제공하며, File.Exists는 존재를 확인하고, GetCreationTime은 타임스탬프를 반환합니다.
13.일반적인 함정(FAQ)
C# 개발자가 가장 자주 빠지는 함정: 값 타입 vs 참조, 문자열 동등성, LINQ 지연, async/await 데드락 등.
문자열 == vs Equal
C#의 string ==는 값으로 비교합니다(Java와 다름). 다만 ==는 서수 비교를 사용하고, Equals는 StringComparison을 지정하여 대소문자 무시 등을 수행할 수 있습니다.
값 전달 vs 참조 전달
메서드 매개변수는 기본적으로 값으로 전달됩니다. 객체는 참조 복사본이 전달되어 멤버를 수정하면 원본에 영향을 주고, 값 타입은 복사됩니다. 이 점을 잊는 것은 흔한 버그입니다.
LINQ 지연 평가
LINQ 쿼리는 지연됩니다. 열거하기 전에는 계산되지 않습니다. IQueryable/IEnumerable을 외부로 전달한 뒤 열거하면 부작용이나 오래된 데이터가 섞일 수 있습니다.
async void와 데드락
이벤트 핸들러는 async void를 사용할 수 있지만 일반 메서드는 Task를 반환해야 합니다. sync 컨텍스트(UI/WinForms)에서 .Result/.Wait()는 데드락을 유발합니다.
예외 삼키기와 재던지기
catch 후 처리하지 않으면 예외를 삼키고, throw ex는 스택을 재설정하여 원래 예외 체인을 잃게 합니다. 로그에는 반드시 InnerException을 남겨야 합니다.
리소스 미해제
Stream/HttpClient/DB 연결을 해제하지 않으면 리소스가 누수됩니다. IDisposable은 항상 using을 사용하거나 사용 후 Dispose합니다.
부동소수점 비교
float/double은 이진 표현이 정확하지 않아 직접 == 비교하면 예상치 못한 결과가 나옵니다. 엡실론 허용차를 사용하거나 정확한 연산에는 decimal을 사용하세요.
반복 중 컬렉션 수정
foreach에서 컬렉션을 Add/Remove하면 InvalidOperationException이 던져집니다. 삭제할 요소를 수집한 후 반복 후 삭제하세요.
Nullable과 Null 참조
Nullable 활성화 후 컴파일러가 null 참조 발견을 도와줍니다. null을 직접 반환하면 모호하므로 ?? / ?.로 폴백을 제공하세요.
로컬 시간 vs UTC
저장/전송하는 시간은 UTC(DateTimeKind.Utc)를 사용하고 표시 시 로컬로 변환합니다. Kind를 혼용하면 조용히 시간이 잘못 계산됩니다.
14.동시성과 비동기
Task 기반 비동기 프로그래밍, Parallel 병렬, lock 동기화, 스레드 안전 컬렉션.
async / await
async 메서드는 Task를 반환하고, await는 스레드를 양보했다가 완료 후 재개합니다. 그 사이에 스레드는 다른 작업에 사용되며 차단되지 않습니다.
Task와 반환값
Task는 비동기 작업을 나타내고, Task<T>는 반환값을 가집니다. Task.Run은 동기 작업을 스레드 풀에 넣고, ContinueWith로 체이닝합니다.
Parallel 병렬
Parallel.For/ForEach는 스레드 풀을 사용해 독립적인 작업을 병렬 실행합니다. CPU 집약적 작업은 멀티 코어를 활용할 수 있지만 스레드 안전성과 과도한 병렬화에 주의하세요.
lock 동기화
lock은 동시에 하나의 스레드만 임계 영역에 들어가도록 보장합니다. 잠금 객체는 보통 private readonly 필드이며, this 잠금은 피하세요.
Thread 스레드
Thread는 스레드를 수동 관리합니다. Start로 시작, Join으로 대기. 대부분의 시나리오에서는 Task/Parallel이 더 적합하며, Thread는 장기 백그라운드 작업에 사용합니다.
CancellationToken
CancellationToken은 협력적 취소입니다. 토큰이 IsCancellationRequested를 트리거하고, async 메서드는 OperationCanceledException을 던집니다.
동시성 컬렉션
스레드 안전 컬렉션: ConcurrentDictionary, ConcurrentQueue, ConcurrentBag. 일반 컬렉션에 수동으로 잠금을 거는 것을 대체합니다.
비동기 모범 사례
Async all the way: .Result로 차단하지 마세요. 라이브러리 코드에서는 ConfigureAwait(false)를 사용하여 동기 컨텍스트로 돌아가지 않도록 합니다.
15.네트워킹과 HTTP
HttpClient로 요청 보내기, JsonSerializer로 직렬화, WebSocket, Socket.
HttpClient 기본
HttpClient는 HTTP 요청을 보냅니다. GetStringAsync는 단순 텍스트, GetAsync는 완전한 응답. HttpClient는 장기(싱글톤) 재사용해야 합니다.
POST와 JSON
PostAsJsonAsync는 JSON을 보내고, PostAsync는 사용자 정의 콘텐츠를 보냅니다. 응답은 ReadAsStringAsync로 읽습니다. 직렬화에는 JsonSerializer를 사용합니다.
헤더와 쿼리 매개변수
HttpRequestMessage는 Headers(인증/User-Agent)를 설정하고, URI는 쿼리 문자열을 구성합니다. 기본 User-Agent를 거부하는 API가 있음에 주의하세요.
System.Text.Json
JsonSerializer.Serialize/Deserialize는 JSON을 직렬화합니다. JsonSerializerOptions는 대소문자/열거형/null 무시를 구성합니다.
Web API 호출
조합: 요청 구성 → 상태 검증 → 역직렬화. HttpStatusCode 검사, ReadFromJsonAsync로 한 줄에 처리합니다.
WebSocket
ClientWebSocket은 전이중 채널을 설정하고, SendAsync/ReceiveAsync로 송수신합니다. 실시간 푸시(채팅, 시세)에 적합합니다.
DNS와 Socket
Dns.GetHostAddresses는 도메인을 해석하고, Socket은 하위 전송입니다. 대부분의 응용 프로그램은 HttpClient로 충분하며, Socket은 사용자 정의 프로토콜용입니다.
다운로드와 스트림 처리
큰 파일 다운로드에는 Stream으로 스트리밍 쓰기하여 메모리를 차지하지 않습니다. HttpCompletionOption.ResponseHeadersRead는 스트림을 즉시 반환합니다.
16.날짜와 시간
DateTime과 TimeSpan, 시간대와 오프셋, 포맷팅과 파싱.
DateTime 기본
DateTime은 날짜/시각을 나타냅니다. Now는 로컬, UtcNow는 UTC, Today는 날짜. AddDays/AddHours로 날짜 연산을 수행합니다.
TimeSpan 시간 간격
TimeSpan은 간격을 나타냅니다. 두 시각의 차이로 얻고, FromDays/FromHours로 구축하며, Days/Hours로 구성 요소를 분해합니다.
DateTimeOffset
DateTimeOffset은 시간대 오프셋을 가지며 시간대를 넘나드는 시나리오에 권장됩니다. UTC와 안전하게 상호 변환되며 로컬 시간대 모호성을 피합니다.
DateTimeKind
DateTime에는 Kind가 있습니다: Unspecified/Local/Utc. Kind 미지정 로컬 시간을 UTC로 변환하면 잘못 동작하므로 Utc로 저장하세요.
파싱과 포맷팅
DateTime.Parse/ParseExact로 파싱, TryParse는 안전합니다. 포맷 지정자: yyyy/MM/dd, HH:mm:ss, ffff는 밀리초.
시간대 변환
TimeZoneInfo는 시간대 변환을 수행합니다: FindSystemTimeZoneById, ConvertTimeFromUtc. 서버는 보통 UTC로 저장하고 표시 시 로컬로 변환합니다.
Stopwatch 측정
Stopwatch는 고정밀(밀리초 단위) 시간 측정을 합니다. Start/Stop/Elapsed/ElapsedMilliseconds는 성능 측정의 표준입니다.
DateOnly와 TimeOnly
DateOnly는 날짜만, TimeOnly는 시각만 나타내는 타입(C# 10)이며 시간대 부담이 없습니다. 생일, 달력, 스케줄에 적합합니다.
17.프로세스와 시스템
외부 프로세스 시작, 환경 변수, 경로, 시스템 정보.
프로세스 시작
Process.Start는 외부 프로그램(셸 명령, 다른 실행 파일)을 시작합니다. ArgumentList로 인자를 전달하면 문자열 연결로 인한 인젝션을 피할 수 있습니다.
환경 변수
Environment.GetEnvironmentVariable로 읽고, SetEnvironmentVariable로 씁니다(프로세스 레벨). GetEnvironmentVariables로 모두 가져옵니다.
시스템 정보
Environment는 시스템 정보를 제공합니다: OSVersion, MachineName, CurrentDirectory, ProcessorCount, TickCount.
명령줄 파싱
args는 명령줄 인자를 전달하고, Environment.GetCommandLineArgs는 완전한 세트(프로그램 이름 포함)를 가져옵니다. CLI 도구에서 인자를 파싱합니다.
종료와 시그널
Environment.Exit(1)은 즉시 종료, ExitCode는 종료 코드를 설정합니다. AppDomain.ProcessExit 이벤트로 정리를 수행합니다.
프로세스 열거
Process.GetProcesses는 시스템 프로세스를 열거하고 Id/ProcessName/WorkingSet64 등의 정보를 읽으며, Kill로 프로세스를 종료합니다.
Windows 레지스트리
Microsoft.Win32.Registry는 레지스트리(Windows 전용)를 읽기/쓰기합니다. GetValue/SetValue로 키 값에 접근하며 적절한 권한이 필요합니다.
응용 프로그램 경로
AppContext.BaseDirectory는 프로그램 실행 디렉터리, Environment.ProcessPath는 현재 프로세스 경로입니다. 리소스 파일 위치 지정에 사용합니다.
18.정규식
Regex 매칭, 캡처 그룹, 치환, 일반 패턴.
Regex 기본
Regex.IsMatch로 판정, Matches로 모두 찾기, Match로 첫 번째 찾기. @ 축자 문자열을 사용하면 이중 이스케이프를 피할 수 있습니다.
캡처 그룹
괄호로 하위 문자열을 캡처하고, Groups[1]로 인덱스 참조, 명명된 그룹 (?<name>...)은 Groups["name"]으로 참조합니다. 필드 추출에 사용됩니다.
치환
Regex.Replace는 패턴으로 치환하며, $1/$2로 캡처 그룹을 참조합니다. 포맷팅, 마스킹, 텍스트 정리에 사용할 수 있습니다.
RegexOptions
RegexOptions.IgnoreCase는 대소문자 무시, Multiline은 ^/$가 줄 단위로 매치, Compiled는 컴파일하여 재사용을 가속합니다.
일반 패턴
이메일, URL, IP, 휴대폰 번호의 일반 정규식. 주의: 복잡한 검증(실제 이메일 등)에는 정규식 대신 전용 라이브러리를 사용하세요.
수량자와 앵커
*는 0회 이상, +는 1회 이상, ?는 0 또는 1회, {n,m}은 지정 횟수. ^는 줄 시작, $는 줄 끝, \b는 단어 경계.
어설션과 룩어라운드
제로 폭 어설션은 문자를 소비하지 않습니다. (?<=...)는 후행, (?=...)는 선행, (?!...)는 부정 선행. 위치 조건 매칭에 사용됩니다.
역추적과 ReDoS
중첩 수량자와 대안 분기는 역추적을 일으키며, 악의적인 입력으로 지수 매칭(ReDoS)이 발생할 수 있습니다. 타임아웃을 제한하거나 역추적을 비활성화해야 합니다.
19.빌드와 디버그
dotnet CLI, 프로젝트 구성, 디버그, 로깅, 조건부 컴파일.
dotnet CLI
dotnet build는 빌드, run은 실행, test는 테스트, publish는 게시. --configuration Release로 릴리스 구성을 게시합니다.
csproj 구성
csproj는 빌드를 제어합니다: TargetFramework, Nullable, ImplicitUsings, PackageReference 의존성, LangVersion.
디버그와 로깅
Debug.WriteLine은 Debug 빌드에서만 동작하고, Trace는 모든 빌드에서 동작합니다. ILogger는 로그 레벨을 기록하며, Console.WriteLine은 임시 디버그에 사용합니다.
조건부 컴파일
#if DEBUG / #elif / #endif로 심볼에 따라 코드를 분기합니다. 프로젝트 레벨 DefineConstants로 사용자 정의 심볼을 정의합니다.
단위 테스트
xUnit/NUnit/MSTest로 동작을 어서트합니다. [Fact]가 테스트 메서드, Assert.Equal이 어서션. dotnet test로 실행합니다.
NuGet 패키지 관리
dotnet add package로 의존성을 추가하고, restore로 복원합니다. PackageReference가 csproj에 버전을 기록합니다.
게시와 단일 파일
dotnet publish는 프로덕션 버전을 게시하며, PublishSingleFile은 단일 파일로 패키징, SelfContained는 런타임 설치가 필요 없습니다.
정적 분석기
Roslyn 분석기는 컴파일 타임에 코드 품질과 잠재적 결함을 검사하며, 경고를 오류로 승격시켜 문제가 있는 빌드를 차단할 수 있습니다.
이 치트시트에 대해
이 페이지는 C# 12(.NET 8)의 자체 완결형 치트시트로, 언어 핵심, 자주 사용되는 BCL 타입, 실제 프로젝트에서 약 80%의 일반적인 사용을 차지하는 비동기 프로그래밍을 다룹니다. 내용은 현대적 관용구에 중점을 둡니다: 속성과 자동 속성, LINQ, async/await, record 타입, 패턴 매칭, nullable 참조 타입. C#은 Anders Hejlsberg가 2000년에 .NET 플랫폼과 함께 출시한 언어로, Windows 생태계, Unity 게임 개발, 백엔드 서비스의 핵심 언어이며 타입 안전성과 생산성의 조화를 강조합니다. 19개 섹션은 각각 한 가지 주제에 집중합니다: 기본 문법, 변수, 타입과 참조/값 의미론, 제어 흐름, 함수, 문자열, 컬렉션, 메모리 관리(GC), 객체 지향, 오류 처리, I/O, 일반적인 함정, 동시성, 네트워킹, 시간, 프로세스, 정규식, 빌드 도구. 각 하위 섹션에는 「개념 소개 + 바로 복사할 수 있는 코드 스니펫」가 함께 제공됩니다. 모든 코드와 텍스트는 브라우저에서 로컬로 렌더링되며, 어떠한 데이터도 기기를 떠나지 않습니다. 권위 있는 참조는 Microsoft Learn 공식 문서를 참조하세요.
버전 2.1.0