코드 작성
개발자가 C# 언어로 소스 코드를 작성한다. 이 코드는 .cs 확장자를 가진 파일에 저장됨
컴파일
C# 컴파일러(csc.exe)는 소스 코드 파일을 컴파일하여 중간 언어(MSIL, Microsoft Intermediate Language, 현재는 IL, Intermediate Language라고 불림)로 변환된 어셈블리를 생성한다. 이 어셈블리는 .dll 또는 .exe 파일 형태로 저장됨.
어셈블리 생성
컴파일러가 생성한 어셈블리는 메타데이터(타입 정보, 버전 정보, 외부 어셈블리 참조 등)와 함께 IL 코드를 포함합니다. IL 코드는 플랫폼 독립적이므로, 다양한 환경에서 실행될 수 있는 이점이 있습니다.
런타임 실행
어플리케이션을 실행하면, .NET Common Language Runtime(CLR)이 어셈블리를 로드합니다. CLR은 JIT 컴파일러(Just-In-Time 컴파일러)를 사용하여 IL 코드를 실행할 수 있는 기계어로 변환. 이 변환은 런타임에 일어나며, 해당 기계어는 호스트 시스템의 CPU가 이해할 수 있는 명령어이다.
관리 코드 실행
CLR은 코드의 실행을 관리한다. 이는 메모리 관리, 스레드 관리, 예외 처리, 가비지 컬렉션 등을 포함합니다. CLR은 .NET 프레임워크의 핵심 구성 요소이며, 코드가 안전하고 효율적으로 실행되도록 한다.
어셈블리 로드와 실행
프로그램이 실행되는 동안, 필요한 추가 어셈블리나 리소스는 필요할 때 동적으로 로드된다. 이 과정에서도 CLR은 코드를 관리하고, 보안과 일관된 실행 환경을 제공한다.
: 데이터의 유형과 크기를 지정하는 것
기본 데이터 형식

복합 데이터 형식


// 공부 필요
: 코드 내의 다양한 요소들(클래스, 구조체, 인터페이스, 열거형 등)을 논리적으로 그룹화하고 이름 충돌을 방지함. 네임스페이스를 통해 동일한 이름의 클래스나 메소드가 다른 네임스페이스에서 독립적으로 존재할 수 있음
namespace MyNamespace
{
class MyClass
{
// 클래스 구현
}
}
namespace AnotherNamespace
{
class MyClass
{
// 다른 구현
}
}
using MyNamespace;class Calculator
{
public static int Plus(int a, int b)
{
int result = a + b;
return result;
}
}
Calculator.plus(1, 2);
// => 3
: enum으로 이름이 열거된 한 그룹을 만들어 연속된 상수의 값을 할당하는 형식으로, 각 멤버 값은 기본적으로 정수형(int)으로 저장됨
enum number { a, b, c, d, e };
// a=0, b=1, c=2, d=3, e=4
class Person
{
// 속성
public string Name;
public int Age;
// 메서드
public void Hi()
{
Console.WriteLine("안녕 나는" + Name + Age + "살이야" );
}
}
// 인스턴스 생성
Person John = new Person();
// 인스턴스 속성에 접근
John.Name = "존 ";
John.Age = "25";
John.Hi();
// 안녕 나는 존 25살이야
: 클래스 이름과 클래스 내부의 메서드 이름을 동일하게 설정하여, 인스턴스를 생성할 때 메서드가 자동으로 호출되게 함
class Cat
{
public string Name;
public Cat(string name)
{
Name = name;
Console.WriteLine(Name);
}
}
Cat myCat = new Cat("나비");
// "나비"
: 하나의 클래스 내에 동일한 이름의 메서드나 생성자를 여러 개 정의할 수 있는 기능
public class Calculator
{
// 첫 번째 Add 메서드: 두 정수의 합을 계산
public int Add(int a, int b)
{
return a + b;
}
// 두 번째 Add 메서드: 세 정수의 합을 계산 (오버로딩)
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
: 클래스가 다른 클래스의 속성과 메서드를 이어 받는 것
// 슈퍼 클래스
public class Car
{
public void StartEngine()
{
Console.WriteLine("부릉 부릉");
}
}
// 서브 클래스
public class Bus : Car
{
...
}
// 상속된 메서드 사용
class Program
{
static void Main(string[] args)
{
Bus myBus = new Bus();
myBus.StartEngine(); // "부릉 부릉" 출력
}
}
: 서브 클래스가 슈퍼 클래스로 부터 상속받은 메서드의 구현을 재정의하는 기능
virtual 키워를 선언함override 키워드를 선언하여 메서드를 재정의base 키워드를 통해 서브 클래스 내에서 슈퍼 클래스의 멤버에 접근 가능// 슈퍼 클래스
public class Car
{
public virtual void StartEngine()
{
Console.WriteLine("부릉 부릉");
}
}
// 서브 클래스
public class Bus : Car
{
public override void StartEngine()
{
base.StartEngine(); // 슈퍼 클래스의 StartEngine 호출
Console.WriteLine("붕붕");
}
}
: 클래스와 비슷하게 여러 데이터(필드, 메소드, 속성 등)를 하나의 단위로 그룹화하지만, 클래스와 다르게 값 형식으로 스택에 저장됨
struct 키워드를 선언하여 사용// 좌표 시스템에서 점을 표현할 때
struct Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
: 튜플은 이름이 없는 구조체로 볼 수 있으며 형식 이름 없이 선언
var 데이터 형식을 사용(var name, var age) = ("Alice", 30);
Console.WriteLine(name, age); // "Alice" 30
var person = (Name: "John", Age: 20);
Console.WriteLine(person.Name, person.Age); // "John" 20
: 클래스가 해야하는 행동(메소드)를 결정하는 클래스의 설계도 같은 것
interface 키워드를 사용하여 선언I로 시작하도록 명명// 인터페이스 선언
interface IAnimal
{
void Speak(string a);
}
// 인터페이스 상속
class Dog : IAnimal
{
public void Speak(string bark)
{
Console.WriteLine(bark);
}
}
: 추상 클래스는 다른 클래스가 상속받아야만 사용할 수 있는 클래스
abstract 키워드를 선언하여 사용abstract class Animal
{
public abstract void Speak(); // 추상 메서드
public void Move()
{
Console.WriteLine("I'm moving");
}
}
class Cat : Animal
{
// 추상 메서드 구현
public override void Speak()
{
Console.WriteLine("Meow");
}
}
: 프로퍼티(Property)는 클래스, 구조체, 인터페이스 내에서 사용되는 특별한 멤버로, 필드(클래스 또는 구조체의 변수)를 캡슐화하여 필드의 값을 읽고 쓰는 방법을 안전하게 제어할 수 있게 해줌
private), 필드의 값을 직접적으로 변경하지 않도록 함public class Person
{
private int age;
public int Age
{
get { return age; }
set
{
if (value < 0)
throw new ArgumentException("존재할 수 없는 나이입니다.");
age = value;
}
}
}
class Program
{
static void Main()
{
Person person = new Person();
person.Age = 30; // set 접근자를 통해 값을 설정
Console.WriteLine(person.Age); // get 접근자를 통해 값을 가져옴
}
}
: 뻔한 코드를 생략하여 사용하는 프로퍼티
private 로 제한할 수 있음public class Person
{
public string Name { get; set; } // 자동 구현 프로퍼티
public int Age { get; private set; } // set 접근자는 private
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
class Program
{
static void Main()
{
Person person = new Person("Alice", 30);
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
person.Name = "Bob"; // Name 프로퍼티는 public set이므로 수정 가능
// person.Age = 35; // 오류: Age 프로퍼티의 set 접근자는 private이므로 외부에서 수정 불가
}
}
: C#에서 데이터의 일시적인 그룹을 만들기 위해 사용되는 기능으로, 주로 로컬 변수로 사용되어 코드 내에서 임시 데이터를 저장하는 데 사용
무명 형식은 new 키워드와 함께 객체 초기자 구문을 사용하여 프로퍼티 이름과 값을 초기화하여 사용
자동으로 형식 추론을 할 수 있게 var 키워드 사용
무명 형식의 프로퍼티는 기본적으로 읽기 전용으로, 한 번 초기화된 후에는 해당 프로퍼티의 값을 변경할 수 없음
로컬 스코프: 무명 형식은 주로 메소드 내에서 임시 데이터를 저장하는 데 사용되며, 메소드 외부로 전달하기 위해서는 일반적으로 object 타입을 사용하거나 LINQ 쿼리의 일부로 사용됨
var person = new { Name = "Alice", Age = 30 };
Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
// person.Name = "Bob"; // 컴파일 오류, 무명 형식의 프로퍼티는 읽기 전용
: 같은 형식의 복수 인스턴스를 저장할 수 있는 참조 형식(Heap)으로 연속된 메모리 공간을 차지함
모든 배열은 System.Array 클래스에서 파생
System.Array 클래스 유틸리티
: Sort, BinarySearch, IndexOf, FindIndex, Clear, Foreach, GetLength, Length, Rank 등
생성방법
// 배열을 생성하는 기본 형식
데이터형식[] 이름 = new 데이터형식[용량]
// example
int[] array1 = new int[3] { 1, 2, 3 }; // 기본
int[] array2 = new int[] { 1, 2, 3 }; // 요소 개수 생략
int[] array3 = { 1, 2, 3 }; // 간소화
: 2개의 차원, 행이 2개인 배열

생성방법
데이터타입[,] 이름 = new 데이터타입[행 개수, 열 개수] {{...}, {...}};
// example
int[,] array1 = new int[2, 3] {{ 1, 2, 3 }, { 4, 5, 6 }};
: 배열의 배열, 배열을 요소로 갖는 배열
생성방법
데이터형식 [][] 이름 = new 데이터형식[가변배열용량][];
// example
int[][] array1 = new int[3][];
: 데이터 그룹을 저장하고 관리하기 위한 클래스의 모음
생성방법
// Collections 네임스페이스에서 가져옴
using System.Collections;
ArrayList 이름 = new ArrayList();
ArrayList는 object 타입을 저장하기 때문에, 정수, 문자열, 객체 등 어떠한 타입의 데이터도 저장할 수 있음
기존 Array와 다르게 배열의 크기를 지정하지 않음
Add() Remove() Insert() 메서드로 접근 가능
생성방법
using System.Collections;
List<T> 이름 = new List<T>();
ArrayList와 비슷하지만 제네릭을 지원
생성방법
using System.Collections;
Queue 이름 = new Queue();
FIFO (First In, First Out) 방식으로 요소를 저장
주로 데이터가 순차적으로 처리되어야 할 때 사용
Enqueue() Dequeue() 메서드로 접근 가능
생성방법
using System.Collections;
Stack 이름 = new Stack();
LIFO (Last In, First Out) 방식으로 요소를 저장
호출 스택, 역순 탐색 등에 사용
Pop() Push() 메서드로 접근 가능
생성방법
using System.Collections;
Hashtable 이름 = new Hashtable();
이름["a"] = 1;
이름["b"] = 2;
이름["c"] = 3;
키와 값의 쌍으로 구성(키는 고유해야 함)
키를 사용하여 값에 접근
현대 C#에서는 Hashtable 대신 제네릭 Dictionary<TKey, TValue> 클래스의 사용이 권장
: 프로그램 실행 중에 발생할 수 있는 예외적인 상황(에러)을 관리하고, 프로그램의 정상적인 흐름을 유지하기 위한 방법
try
{
// 예외가 발생할 수 있는 코드
int a = 10;
int b = 0;
if(a > 10) {
int result = a / b;
}
else {
// 예외처리되어 throw문이 실행되고 범용catch문으로 넘어감
throw new Exception("a가 10보다 작습니다.");
}
}
catch (DivideByZeroException ex)
{
// 특정 예외 처리
Console.WriteLine("0으로 나눌 수 없습니다: " + ex.Message);
}
catch (Exception ex)
{
// 모든 예외 처리
Console.WriteLine("예외 발생: " + ex.Message);
}
finally
{
// 항상 실행되는 코드 (예: 리소스 해제)
Console.WriteLine("처리 완료");
}
: 제네릭 타입 매개변수는 특정 타입을 가리키는 '자리 표시자'와 같은 역할로, 제네릭 타입을 사용하는 클래스나 메서드는 이 매개변수에 어떤 구체적인 타입이 들어가는지에 따라 행동이 결정됨
기본 데이터 타입
: int, string, bool과 같은 기본 데이터 타입을 제네릭 매개변수로 사용할 수 있습니다. 예를 들어, List는 정수형 리스트를, List은 문자열 리스트를 나타냅니다.
사용자 정의 클래스 타입
: 사용자가 정의한 클래스도 제네릭 매개변수로 사용할 수 있습니다. 예를 들어, Guest라는 클래스가 있다면 List는 Guest 객체의 리스트를 나타냅니다.
인터페이스 타입
: 인터페이스도 제네릭 매개변수로 사용될 수 있습니다. 예를 들어, ILogger에서 HomeController는 클래스 타입이며, 이는 ILogger 인터페이스가 HomeController 클래스에 특화된 로깅 기능을 제공한다는 것을 의미합니다.
: 특정한 타입에 국한되지 않고, 다양한 타입에 대해 유연하게 동작할 수 있는 메서드를 정의하는 방식
public T MyGenericMethod<T>(T param)
{
return param;
}
// MyGenericMethod는 일반화 메서드입니다.
// <T>는 타입 매개변수를 나타내며, 메서드 내에서 T 타입으로 사용됩니다.
int result = MyGenericMethod<int>(5);
string resultString = MyGenericMethod<string>("Hello");
// 같은 메서드지만 타입 인수를 다르게하여 호출하였다.
// 타입인수가 없어도 컴파일러가 타입 추론(type inference)할 수 있다.
public class MyClass
{
// 일반화 메서드
public void Method<T>(T param)
{
Console.WriteLine(param);
}
// string 타입에 대한 오버로딩 메서드
public void Method(string param)
{
Console.WriteLine(param);
}
}
: 메서드를 변수처럼 참조할 수 있게 해주는 타입으로, 메서드를 인자로 전달하거나 변수에 할당할 수 있음
접근한정자 delegate 반환형식 이름 (매개변수목록);
// example
public delegate void MyDelegate(string message);
public class Program
{
public static void Main()
{
MyDelegate del = ShowMessage;
del("Hello World");
}
public static void ShowMessage(string message)
{
Console.WriteLine(message);
}
}
식 람다(Expression Lambda)
: 단일 표현식을 사용하여 값을 반환
Func<int, int> square = x => x * x;
문 람다(Statement Lambda)
: 중괄호 {}를 사용하여 여러 문을 포함할 수 있으며, return 문을 사용하여 값을 반환함
Action<int> printSquare = x => {
int result = x * x;
Console.WriteLine(result);
};
- .NET 라이브러리에 사전 정의되어있는 대리자
- 익명 메소드, 무명 함수 정의를 위해 매번 대리자를 새롭게 정의할 필요 없음
- 일반화와 최대 16개 매개변수를 지원(오버로딩)
Func 대리자
: 반환 값이 있는 대리자
Func<int, bool> isPositive = number => number > 0;

Action 대리자
: 반환 값이 없는 대리자
Action<int> printSquare = x => {
int result = x * x;
Console.WriteLine(result);
};
- 직관적으로 해석하면 C# 언어에 데이터 질의 기능이 통합된 것을 의미한다.
- 데이터 집합에서 원하는 데이터를 찾는 작업
var query = from p in people
where p.Age > 18
orderby p.LastName descending // 기본적으로 ascending
select p;
// groupby
from person in people
group person by person.LastName into lastNameGroup
select lastNameGroup
// join
from person in people
join pet in pets on person equals pet.Owner
select new { person.Name, pet.Name }
var evenNumbers = numbers.Where(n => n % 2 == 0);