C# 문법 정리: 기초부터 고급까지
C#은 Microsoft에서 개발한 객체지향 프로그래밍 언어로, .NET 플랫폼의 핵심 언어이다. 이 글은 C# 문법을 기초부터 고급 기능까지 체계적으로 정리한다. 각 개념은 실제 코드 예제와 함께 설명하여 실무에서 바로 활용할 수 있도록 구성하였다.
1. 기본 문법 (Basic Syntax)
1.1 변수와 타입 (Variables and Types)
C#은 정적 타입 언어로, 모든 변수는 명시적으로 타입을 선언해야 한다.
기본 타입 (Primitive Types)
// 정수 타입
byte b = 255; // 0 ~ 255 (8-bit)
sbyte sb = -128; // -128 ~ 127 (8-bit signed)
short s = -32768; // -32,768 ~ 32,767 (16-bit)
ushort us = 65535; // 0 ~ 65,535 (16-bit unsigned)
int i = -2147483648; // -2,147,483,648 ~ 2,147,483,647 (32-bit)
uint ui = 4294967295; // 0 ~ 4,294,967,295 (32-bit unsigned)
long l = -9223372036854775808L; // 64-bit
ulong ul = 18446744073709551615UL; // 64-bit unsigned
// 부동소수점 타입
float f = 3.14f; // 32-bit (접미사 f 필수)
double d = 3.141592653589793; // 64-bit
decimal dec = 3.14159265358979323846m; // 128-bit (접미사 m 필수, 금융 계산용)
// 문자 타입
char c = 'A'; // 16-bit Unicode
string str = "Hello, World!"; // 참조 타입
// 불리언 타입
bool flag = true; // true 또는 false
// 객체 타입
object obj = new object(); // 모든 타입의 기본 클래스
타입 추론 (Type Inference)
C# 3.0부터 var 키워드를 사용하여 타입 추론이 가능하다.
var number = 42; // int로 추론
var name = "C#"; // string으로 추론
var list = new List<int>(); // List<int>로 추론
// var는 지역 변수에만 사용 가능
// var는 초기화와 함께 선언되어야 함
// var x; // 컴파일 오류
상수 (Constants)
const int MaxValue = 100;
const string AppName = "MyApp";
const double Pi = 3.14159;
// readonly 필드 (런타임 상수)
readonly int ReadOnlyValue;
public MyClass(int value)
{
ReadOnlyValue = value; // 생성자에서만 할당 가능
}
1.2 연산자 (Operators)
산술 연산자
int a = 10, b = 3;
int sum = a + b; // 13
int diff = a - b; // 7
int prod = a * b; // 30
int quot = a / b; // 3 (정수 나눗셈)
int rem = a % b; // 1 (나머지)
// 증감 연산자
int x = 5;
x++; // x = 6 (후위)
++x; // x = 7 (전위)
x--; // x = 6 (후위)
--x; // x = 5 (전위)
비교 연산자
int a = 10, b = 20;
bool eq = a == b; // false (같음)
bool ne = a != b; // true (다름)
bool gt = a > b; // false (큼)
bool lt = a < b; // true (작음)
bool ge = a >= b; // false (크거나 같음)
bool le = a <= b; // true (작거나 같음)
논리 연산자
bool p = true, q = false;
bool and = p && q; // false (논리 AND)
bool or = p || q; // true (논리 OR)
bool not = !p; // false (논리 NOT)
// 비트 연산자
int x = 5, y = 3; // 5 = 101, 3 = 011
int bitAnd = x & y; // 1 (001)
int bitOr = x | y; // 7 (111)
int bitXor = x ^ y; // 6 (110)
int bitNot = ~x; // -6 (비트 반전)
int leftShift = x << 1; // 10 (왼쪽 시프트)
int rightShift = x >> 1; // 2 (오른쪽 시프트)
null 관련 연산자
// null 조건부 연산자 (?.)
string str = null;
int? length = str?.Length; // null (NullReferenceException 방지)
// null 병합 연산자 (??)
string name = null;
string displayName = name ?? "Unknown"; // "Unknown"
// null 병합 할당 연산자 (??=)
string value = null;
value ??= "Default"; // value가 null이면 "Default" 할당
1.3 문자열 (Strings)
// 문자열 연결
string firstName = "John";
string lastName = "Doe";
string fullName = firstName + " " + lastName; // "John Doe"
// 문자열 보간 (String Interpolation)
string message = $"Hello, {firstName} {lastName}!"; // C# 6.0+
// 문자열 포맷
string formatted = string.Format("Value: {0}, Name: {1}", 42, "Test");
// 문자열 메서드
string text = "Hello, World!";
int length = text.Length; // 13
string upper = text.ToUpper(); // "HELLO, WORLD!"
string lower = text.ToLower(); // "hello, world!"
bool contains = text.Contains("World"); // true
string substring = text.Substring(0, 5); // "Hello"
string[] parts = text.Split(','); // ["Hello", " World!"]
string trimmed = " text ".Trim(); // "text"
bool starts = text.StartsWith("Hello"); // true
bool ends = text.EndsWith("!"); // true
int index = text.IndexOf("World"); // 7
string replaced = text.Replace("World", "C#"); // "Hello, C#!"
2. 제어문 (Control Flow)
2.1 조건문 (Conditional Statements)
if-else
int score = 85;
if (score >= 90)
{
Console.WriteLine("A");
}
else if (score >= 80)
{
Console.WriteLine("B");
}
else if (score >= 70)
{
Console.WriteLine("C");
}
else
{
Console.WriteLine("F");
}
// 삼항 연산자
string grade = score >= 60 ? "Pass" : "Fail";
switch
int day = 3;
string dayName;
switch (day)
{
case 1:
dayName = "Monday";
break;
case 2:
dayName = "Tuesday";
break;
case 3:
dayName = "Wednesday";
break;
default:
dayName = "Unknown";
break;
}
// switch 표현식 (C# 8.0+)
string dayName2 = day switch
{
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
_ => "Unknown"
};
// 패턴 매칭 (C# 7.0+)
object obj = 42;
switch (obj)
{
case int i when i > 0:
Console.WriteLine($"Positive integer: {i}");
break;
case string s:
Console.WriteLine($"String: {s}");
break;
case null:
Console.WriteLine("Null");
break;
default:
Console.WriteLine("Unknown");
break;
}
2.2 반복문 (Loops)
for
// 기본 for 루프
for (int i = 0; i < 10; i++)
{
Console.WriteLine(i);
}
// 중첩 for 루프
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
Console.WriteLine($"({i}, {j})");
}
}
while
int count = 0;
while (count < 5)
{
Console.WriteLine(count);
count++;
}
// do-while
int num = 0;
do
{
Console.WriteLine(num);
num++;
} while (num < 5);
foreach
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (int num in numbers)
{
Console.WriteLine(num);
}
// 컬렉션 순회
List<string> names = new List<string> { "Alice", "Bob", "Charlie" };
foreach (string name in names)
{
Console.WriteLine(name);
}
// 인덱스와 함께 순회
for (int i = 0; i < names.Count; i++)
{
Console.WriteLine($"{i}: {names[i]}");
}
반복문 제어
// break: 루프 종료
for (int i = 0; i < 10; i++)
{
if (i == 5)
break; // 루프 종료
Console.WriteLine(i);
}
// continue: 다음 반복으로 건너뛰기
for (int i = 0; i < 10; i++)
{
if (i % 2 == 0)
continue; // 짝수는 건너뛰기
Console.WriteLine(i);
}
// goto (권장하지 않음)
for (int i = 0; i < 10; i++)
{
if (i == 5)
goto EndLoop;
Console.WriteLine(i);
}
EndLoop:
Console.WriteLine("Loop ended");
3. 배열과 컬렉션 (Arrays and Collections)
3.1 배열 (Arrays)
// 배열 선언 및 초기화
int[] numbers = new int[5]; // 크기 5, 기본값 0
int[] numbers2 = new int[] { 1, 2, 3, 4, 5 };
int[] numbers3 = { 1, 2, 3, 4, 5 }; // 간단한 문법
// 다차원 배열
int[,] matrix = new int[3, 3]; // 2차원 배열
int[,,] cube = new int[2, 2, 2]; // 3차원 배열
// 가변 배열 (Jagged Array)
int[][] jagged = new int[3][];
jagged[0] = new int[] { 1, 2, 3 };
jagged[1] = new int[] { 4, 5 };
jagged[2] = new int[] { 6, 7, 8, 9 };
// 배열 메서드
int[] arr = { 3, 1, 4, 1, 5 };
Array.Sort(arr); // 정렬
Array.Reverse(arr); // 역순
int index = Array.IndexOf(arr, 4); // 인덱스 찾기
int length = arr.Length; // 길이
3.2 리스트 (List)
// List 선언 및 초기화
List<int> numbers = new List<int>();
List<string> names = new List<string> { "Alice", "Bob" };
// 요소 추가/제거
numbers.Add(1);
numbers.AddRange(new int[] { 2, 3, 4 });
numbers.Insert(0, 0); // 인덱스 0에 삽입
numbers.Remove(3); // 값 3 제거
numbers.RemoveAt(0); // 인덱스 0 제거
numbers.Clear(); // 모두 제거
// 요소 접근
int first = numbers[0]; // 인덱스로 접근
bool contains = numbers.Contains(2); // 포함 여부
int count = numbers.Count; // 개수
int index = numbers.IndexOf(2); // 인덱스 찾기
// 정렬 및 검색
numbers.Sort(); // 정렬
numbers.Reverse(); // 역순
bool exists = numbers.Exists(x => x > 5); // 조건 검색
List<int> filtered = numbers.FindAll(x => x > 2); // 필터링
3.3 딕셔너리 (Dictionary)
// Dictionary 선언 및 초기화
Dictionary<string, int> ages = new Dictionary<string, int>();
Dictionary<string, string> capitals = new Dictionary<string, string>
{
{ "Korea", "Seoul" },
{ "Japan", "Tokyo" },
{ "USA", "Washington" }
};
// 요소 추가/제거
ages["Alice"] = 25;
ages.Add("Bob", 30);
ages.Remove("Alice");
bool removed = ages.Remove("Bob");
// 요소 접근
int age = ages["Alice"]; // 키로 접근
bool hasKey = ages.ContainsKey("Bob"); // 키 존재 여부
bool hasValue = ages.ContainsValue(25); // 값 존재 여부
// 순회
foreach (var kvp in ages)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
foreach (string key in ages.Keys)
{
Console.WriteLine(key);
}
foreach (int value in ages.Values)
{
Console.WriteLine(value);
}
3.4 기타 컬렉션
// HashSet (중복 없는 집합)
HashSet<int> set = new HashSet<int> { 1, 2, 3, 3 }; // {1, 2, 3}
set.Add(4);
bool added = set.Add(2); // false (이미 존재)
// Queue (FIFO)
Queue<string> queue = new Queue<string>();
queue.Enqueue("First");
queue.Enqueue("Second");
string first = queue.Dequeue(); // "First"
string peek = queue.Peek(); // "Second" (제거하지 않음)
// Stack (LIFO)
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
int top = stack.Pop(); // 2
int peek2 = stack.Peek(); // 1 (제거하지 않음)
4. 클래스와 객체 (Classes and Objects)
4.1 클래스 정의
// 기본 클래스
public class Person
{
// 필드 (Fields)
private string name;
private int age;
// 속성 (Properties)
public string Name
{
get { return name; }
set { name = value; }
}
public int Age
{
get => age;
set => age = value;
}
// 자동 속성 (Auto-Property)
public string Email { get; set; }
// 읽기 전용 속성
public string FullName { get; private set; }
// 생성자 (Constructor)
public Person()
{
name = "Unknown";
age = 0;
}
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
// 메서드 (Methods)
public void Introduce()
{
Console.WriteLine($"I'm {name}, {age} years old.");
}
public string GetInfo()
{
return $"{name} ({age})";
}
}
// 사용 예제
Person person = new Person("Alice", 25);
person.Introduce();
4.2 접근 제한자 (Access Modifiers)
public class Example
{
public int PublicField; // 어디서나 접근 가능
private int PrivateField; // 같은 클래스 내에서만
protected int ProtectedField; // 같은 클래스 및 파생 클래스
internal int InternalField; // 같은 어셈블리 내에서만
protected internal int ProtectedInternalField; // protected 또는 internal
private protected int PrivateProtectedField; // private 또는 protected (C# 7.2+)
}
4.3 정적 멤버 (Static Members)
public class MathHelper
{
// 정적 필드
public static int Count = 0;
// 정적 메서드
public static int Add(int a, int b)
{
return a + b;
}
// 정적 속성
public static double Pi { get; } = 3.14159;
// 정적 생성자
static MathHelper()
{
Count = 0;
}
}
// 사용
int result = MathHelper.Add(5, 3);
double pi = MathHelper.Pi;
4.4 생성자와 소멸자
public class MyClass
{
private string name;
private int value;
// 기본 생성자
public MyClass()
{
name = "Default";
value = 0;
}
// 매개변수 생성자
public MyClass(string name, int value)
{
this.name = name;
this.value = value;
}
// 생성자 체이닝
public MyClass(string name) : this(name, 0)
{
}
// 소멸자 (Finalizer)
~MyClass()
{
// 리소스 정리 (권장하지 않음, IDisposable 사용 권장)
}
}
5. 상속과 다형성 (Inheritance and Polymorphism)
5.1 상속 (Inheritance)
// 기본 클래스
public class Animal
{
public string Name { get; set; }
public int Age { get; set; }
public Animal(string name, int age)
{
Name = name;
Age = age;
}
public virtual void MakeSound()
{
Console.WriteLine("Some sound");
}
public void Eat()
{
Console.WriteLine($"{Name} is eating.");
}
}
// 파생 클래스
public class Dog : Animal
{
public string Breed { get; set; }
public Dog(string name, int age, string breed)
: base(name, age)
{
Breed = breed;
}
// 메서드 오버라이딩
public override void MakeSound()
{
Console.WriteLine("Woof!");
}
// 새 메서드 추가
public void Bark()
{
Console.WriteLine($"{Name} is barking!");
}
}
// 사용
Dog dog = new Dog("Buddy", 3, "Golden Retriever");
dog.MakeSound(); // "Woof!"
dog.Eat(); // "Buddy is eating."
5.2 추상 클래스 (Abstract Classes)
// 추상 클래스
public abstract class Shape
{
public abstract double GetArea();
public abstract double GetPerimeter();
public virtual void Draw()
{
Console.WriteLine("Drawing a shape");
}
}
// 추상 클래스 구현
public class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius)
{
Radius = radius;
}
public override double GetArea()
{
return Math.PI * Radius * Radius;
}
public override double GetPerimeter()
{
return 2 * Math.PI * Radius;
}
}
5.3 인터페이스 (Interfaces)
// 인터페이스 정의
public interface IFlyable
{
void Fly();
int MaxAltitude { get; set; }
}
public interface ISwimmable
{
void Swim();
}
// 인터페이스 구현
public class Duck : IFlyable, ISwimmable
{
public int MaxAltitude { get; set; } = 1000;
public void Fly()
{
Console.WriteLine("Duck is flying");
}
public void Swim()
{
Console.WriteLine("Duck is swimming");
}
}
// 인터페이스 사용
IFlyable flyable = new Duck();
flyable.Fly();
5.4 다형성 (Polymorphism)
// 다형성 예제
Animal[] animals = new Animal[]
{
new Dog("Buddy", 3, "Golden Retriever"),
new Cat("Whiskers", 2)
};
foreach (Animal animal in animals)
{
animal.MakeSound(); // 각 객체의 오버라이딩된 메서드 호출
}
6. 제네릭 (Generics)
6.1 제네릭 클래스
// 제네릭 클래스
public class Box<T>
{
private T item;
public void SetItem(T item)
{
this.item = item;
}
public T GetItem()
{
return item;
}
}
// 사용
Box<int> intBox = new Box<int>();
intBox.SetItem(42);
int value = intBox.GetItem();
Box<string> stringBox = new Box<string>();
stringBox.SetItem("Hello");
string text = stringBox.GetItem();
6.2 제네릭 메서드
public class Utility
{
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
public static T Max<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b) > 0 ? a : b;
}
}
// 사용
int x = 5, y = 10;
Utility.Swap(ref x, ref y); // x = 10, y = 5
string s1 = "apple", s2 = "banana";
string max = Utility.Max(s1, s2); // "banana"
6.3 제네릭 제약 (Constraints)
// where 제약
public class Repository<T> where T : class
{
private List<T> items = new List<T>();
public void Add(T item)
{
items.Add(item);
}
}
// 여러 제약
public class Processor<T> where T : class, new()
{
public T Create()
{
return new T();
}
}
// 인터페이스 제약
public class Sorter<T> where T : IComparable<T>
{
public void Sort(List<T> list)
{
list.Sort();
}
}
7. 델리게이트와 이벤트 (Delegates and Events)
7.1 델리게이트 (Delegates)
// 델리게이트 정의
public delegate void MyDelegate(string message);
public delegate int Calculate(int a, int b);
// 델리게이트 사용
public class Calculator
{
public static int Add(int a, int b) => a + b;
public static int Multiply(int a, int b) => a * b;
}
Calculate calc = Calculator.Add;
int result = calc(5, 3); // 8
calc = Calculator.Multiply;
result = calc(5, 3); // 15
// 멀티캐스트 델리게이트
MyDelegate del = PrintMessage;
del += PrintMessage2;
del("Hello"); // 두 메서드 모두 호출
void PrintMessage(string msg) => Console.WriteLine($"Message 1: {msg}");
void PrintMessage2(string msg) => Console.WriteLine($"Message 2: {msg}");
7.2 Func와 Action
// Func: 반환값이 있는 델리게이트
Func<int, int, int> add = (a, b) => a + b;
int sum = add(5, 3); // 8
Func<string, int> getLength = s => s.Length;
int len = getLength("Hello"); // 5
// Action: 반환값이 없는 델리게이트
Action<string> print = s => Console.WriteLine(s);
print("Hello, World!");
Action<int, int> printSum = (a, b) => Console.WriteLine(a + b);
printSum(5, 3); // 8 출력
7.3 이벤트 (Events)
// 이벤트 정의
public class Button
{
public event EventHandler Clicked;
public void Click()
{
Clicked?.Invoke(this, EventArgs.Empty);
}
}
// 이벤트 구독
Button button = new Button();
button.Clicked += (sender, e) => Console.WriteLine("Button clicked!");
button.Click(); // "Button clicked!" 출력
// 커스텀 이벤트 인자
public class TemperatureChangedEventArgs : EventArgs
{
public double OldTemperature { get; set; }
public double NewTemperature { get; set; }
}
public class Thermometer
{
public event EventHandler<TemperatureChangedEventArgs> TemperatureChanged;
private double temperature;
public double Temperature
{
get => temperature;
set
{
if (temperature != value)
{
double oldTemp = temperature;
temperature = value;
TemperatureChanged?.Invoke(this,
new TemperatureChangedEventArgs
{
OldTemperature = oldTemp,
NewTemperature = value
});
}
}
}
}
8. LINQ (Language Integrated Query)
8.1 기본 LINQ 쿼리
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Where: 필터링
var evens = numbers.Where(n => n % 2 == 0); // [2, 4, 6, 8, 10]
// Select: 변환
var squares = numbers.Select(n => n * n); // [1, 4, 9, 16, ...]
// OrderBy: 정렬
var sorted = numbers.OrderBy(n => n); // 오름차순
var desc = numbers.OrderByDescending(n => n); // 내림차순
// First/Last: 첫/마지막 요소
int first = numbers.First(); // 1
int last = numbers.Last(); // 10
int firstEven = numbers.First(n => n % 2 == 0); // 2
// Any/All: 조건 검사
bool hasEven = numbers.Any(n => n % 2 == 0); // true
bool allPositive = numbers.All(n => n > 0); // true
// Count: 개수
int count = numbers.Count(); // 10
int evenCount = numbers.Count(n => n % 2 == 0); // 5
// Sum/Average/Max/Min: 집계
int sum = numbers.Sum(); // 55
double avg = numbers.Average(); // 5.5
int max = numbers.Max(); // 10
int min = numbers.Min(); // 1
8.2 LINQ 쿼리 구문
List<Person> people = new List<Person>
{
new Person { Name = "Alice", Age = 25 },
new Person { Name = "Bob", Age = 30 },
new Person { Name = "Charlie", Age = 25 }
};
// 쿼리 구문
var query = from p in people
where p.Age > 25
orderby p.Name
select p.Name;
// 메서드 구문 (동일한 결과)
var query2 = people
.Where(p => p.Age > 25)
.OrderBy(p => p.Name)
.Select(p => p.Name);
// Join
var joined = from p in people
join c in cities on p.CityId equals c.Id
select new { p.Name, c.CityName };
// GroupBy
var grouped = from p in people
group p by p.Age into g
select new { Age = g.Key, Count = g.Count() };
9. 예외 처리 (Exception Handling)
9.1 try-catch-finally
try
{
int result = 10 / 0; // DivideByZeroException 발생
}
catch (DivideByZeroException ex)
{
Console.WriteLine($"Division by zero: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"General error: {ex.Message}");
}
finally
{
Console.WriteLine("This always executes");
}
// 여러 예외 처리
try
{
// 코드
}
catch (ArgumentNullException ex)
{
// null 인자 예외 처리
}
catch (ArgumentException ex)
{
// 인자 예외 처리
}
catch (Exception ex)
{
// 기타 예외 처리
}
9.2 사용자 정의 예외
// 사용자 정의 예외 클래스
public class InvalidAgeException : Exception
{
public InvalidAgeException(string message) : base(message)
{
}
public InvalidAgeException(string message, Exception innerException)
: base(message, innerException)
{
}
}
// 예외 발생
public void SetAge(int age)
{
if (age < 0 || age > 150)
{
throw new InvalidAgeException($"Invalid age: {age}");
}
this.age = age;
}
9.3 using 문 (IDisposable)
// using 문으로 리소스 자동 해제
using (var file = new StreamReader("file.txt"))
{
string content = file.ReadToEnd();
} // 자동으로 Dispose() 호출
// using 선언 (C# 8.0+)
using var file = new StreamReader("file.txt");
string content = file.ReadToEnd();
// 블록 종료 시 자동 Dispose
10. 비동기 프로그래밍 (Async/Await)
10.1 async/await 기본
// 비동기 메서드
public async Task<string> FetchDataAsync()
{
await Task.Delay(1000); // 1초 대기
return "Data loaded";
}
// 사용
string data = await FetchDataAsync();
Console.WriteLine(data);
// 여러 비동기 작업
public async Task<string> FetchMultipleDataAsync()
{
var task1 = FetchDataAsync();
var task2 = FetchDataAsync();
await Task.WhenAll(task1, task2);
return $"{task1.Result} and {task2.Result}";
}
// 비동기 void (이벤트 핸들러에서만 사용)
public async void Button_Click(object sender, EventArgs e)
{
await SomeAsyncMethod();
}
10.2 Task와 Task
// Task 생성
Task task = Task.Run(() =>
{
Console.WriteLine("Running in background");
});
await task;
// Task<T> 반환
Task<int> taskWithResult = Task.Run(() =>
{
return 42;
});
int result = await taskWithResult;
// 병렬 실행
var tasks = new List<Task<int>>();
for (int i = 0; i < 10; i++)
{
int index = i;
tasks.Add(Task.Run(() => index * 2));
}
int[] results = await Task.WhenAll(tasks);
11. 고급 기능
11.1 확장 메서드 (Extension Methods)
// 확장 메서드 정의
public static class StringExtensions
{
public static bool IsValidEmail(this string email)
{
return email.Contains("@") && email.Contains(".");
}
public static string Reverse(this string str)
{
char[] chars = str.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
// 사용
string email = "test@example.com";
bool isValid = email.IsValidEmail(); // true
string reversed = email.Reverse(); // "moc.elpmaxe@tset"
11.2 람다 표현식 (Lambda Expressions)
// 람다 표현식
Func<int, int> square = x => x * x;
int result = square(5); // 25
// 여러 매개변수
Func<int, int, int> add = (x, y) => x + y;
int sum = add(3, 4); // 7
// 문장 본문
Action<string> print = message =>
{
Console.WriteLine($"Message: {message}");
};
// LINQ와 함께 사용
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evens = numbers.Where(n => n % 2 == 0);
var squares = numbers.Select(n => n * n);
11.3 null 조건부 연산자와 null 병합
// null 조건부 연산자
string str = null;
int? length = str?.Length; // null (NullReferenceException 방지)
// null 병합 연산자
string name = null;
string displayName = name ?? "Unknown"; // "Unknown"
// null 병합 할당
string value = null;
value ??= "Default"; // value가 null이면 "Default" 할당
// 체이닝
string result = person?.Address?.City ?? "Unknown";
11.4 패턴 매칭 (Pattern Matching)
// is 패턴 (C# 7.0+)
object obj = 42;
if (obj is int i)
{
Console.WriteLine($"Integer: {i}");
}
// switch 패턴 (C# 7.0+)
object value = 42;
switch (value)
{
case int i when i > 0:
Console.WriteLine($"Positive integer: {i}");
break;
case string s:
Console.WriteLine($"String: {s}");
break;
case null:
Console.WriteLine("Null");
break;
default:
Console.WriteLine("Unknown");
break;
}
// switch 표현식 (C# 8.0+)
string result = value switch
{
int i when i > 0 => $"Positive: {i}",
int i => $"Integer: {i}",
string s => $"String: {s}",
null => "Null",
_ => "Unknown"
};
11.5 레코드 (Records) - C# 9.0+
// 레코드 정의
public record Person(string Name, int Age);
// 사용
var person1 = new Person("Alice", 25);
var person2 = new Person("Alice", 25);
bool equal = person1 == person2; // true (값 기반 비교)
// with 표현식
var person3 = person1 with { Age = 26 };
// 레코드 상속
public record Student(string Name, int Age, string School) : Person(Name, Age);
11.6 init 접근자 - C# 9.0+
public class Person
{
public string Name { get; init; } // 객체 초기화 시에만 설정 가능
public int Age { get; init; }
}
// 사용
var person = new Person { Name = "Alice", Age = 25 };
// person.Name = "Bob"; // 컴파일 오류 (init은 초기화 후 변경 불가)
12. 네임스페이스와 어셈블리
12.1 네임스페이스
// 네임스페이스 정의
namespace MyCompany.MyProject
{
public class MyClass
{
// ...
}
}
// 네임스페이스 별칭
using Project = MyCompany.MyProject;
Project.MyClass obj = new Project.MyClass();
// 전역 using (C# 10.0+)
global using System;
global using System.Collections.Generic;
12.2 어셈블리와 참조
// 다른 어셈블리의 클래스 사용
using System;
using System.Collections.Generic;
// NuGet 패키지 참조
// dotnet add package Newtonsoft.Json
using Newtonsoft.Json;
var json = JsonConvert.SerializeObject(new { Name = "Test" });
13. 속성과 리플렉션 (Attributes and Reflection)
13.1 속성 (Attributes)
// 속성 정의
[Serializable]
public class MyClass
{
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }
[Conditional("DEBUG")]
public void DebugMethod() { }
}
// 사용자 정의 속성
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MyAttribute : Attribute
{
public string Description { get; set; }
}
[My(Description = "Test class")]
public class TestClass { }
13.2 리플렉션
// 타입 정보 가져오기
Type type = typeof(MyClass);
// 속성 정보
var attributes = type.GetCustomAttributes(typeof(MyAttribute), false);
// 메서드 정보
var methods = type.GetMethods();
// 인스턴스 생성
object instance = Activator.CreateInstance(type);
// 속성 설정
PropertyInfo prop = type.GetProperty("Name");
prop.SetValue(instance, "Test");
14. 결론
C#은 현대적인 객체지향 프로그래밍 언어로, 강력한 타입 시스템, 풍부한 표준 라이브러리, 그리고 지속적인 언어 개선을 통해 개발자에게 효율적인 개발 환경을 제공한다. 이 글에서 다룬 내용은 C#의 핵심 문법과 기능들이며, 실무에서 자주 사용되는 패턴들을 포함하고 있다. 각 기능을 실제 프로젝트에 적용하면서 더 깊이 있는 이해를 얻을 수 있다.