객체지향 프로그래밍
C#은 객체지향 프로그래밍 개념을 채택한다.
OOP 개념
- Object Oriented Programming
- Encapsulation (캡슐화)
- 메소드와 속성을 묶어 하나의 unit으로 구성하는 방식.
- 객체 내부를 외부에 감추는 방식으로 기능을 단순화할 수 있음.
- Polymorphism (다형성)
- 동일한 요청에 서로 다른 방식으로 작동할 수 있도록 하는 성질.
Overloading
: 같은 이름이지만 return타입, parameter 등이 다른 메소드. 컴파일 타임에 호출할 메소드가 결정됨.Overriding
: 부모로부터 상속받은 메소드에 대한 재정의. 런타임에 호출할 메소드가 동적으로 결정되기 떄문에 런타임에 메모리 할당이 일어남.
- Inheritance (상속)
- 상위 클래스에서 사용하는 기능과 속성을 하위 클래스에 제공하고, 하위 클래스는 그를 확장하여 기능할 수 있는 방식.
- 상위 클래스는 공통된 속성이나 추상화된 기능만 가지고 있어야 함.
- Abstract로 정의할 수 있음. 추상클래스로 정의된 경우, 메소드 정의를 포함하지 않아도 됨.
값 타입 & 참조 타입
- 값 타입은 선언과 동시에 할당, 참조 타입은 객체를 생성해야 할당됨.
- Boxing
- 값 타입을 참조타입으로 변환하는 것.
- 힙에 메모리 할당 -> 값 타입이 힙으로 복사됨 -> 메모리 주소가 반환됨 -> 주소를 스택에 저장
- 언박싱 시에는 참조타입을 casting하여 값 타입으로 변환.
- C#에서
ArrayList
는 element를 object로 다루기 때문에 데이터를 사용할 때 박싱, 언박싱이 발생. - Generic은 박싱, 언박싱으로 인한 오버헤드가 발생하지 않음.
- Casting
Implicit conversion
: 값이 생략될 필요가 없는 경우의 변환. (ex. int -> long)Explicit conversion
: 값이 손상되는 변환. (ex. double -> float)as
키워드를 이용하여 캐스팅이 가능함.
object o = 47;
var i = o as int?;
Console.WriteLine(i ?? -1); // 47
o = "47";
i = o as int?;
Console.WriteLine(i ?? -1); // -1
부동소수점 (floating point)
double d = 123.123123;
float f = (float)d; // 123.12312
d = f; // 123.12312316894531
- 부동소수점 참고 자료
- 실수를 컴퓨터상에서 근사하여 표현할 때, 소수점의 위치를 고정하지 않고 그 위치를 나타내는 수를 따로 적는 것.
- 2진수로 다 표현할 수 없는 값에 대해, 근사로 표현.
부호
(1bit) +지수
(8bit) +가수
(23bit) (총 4byte)로 구성.가수 * 밑수 ^ 지수
(밑수가 10인 경우에 E로 표기하기도 함)- 고정소수점은 정수 표현부, 소수 표현부의 비트 수를 고정하는 방식.
Anonymous type
- 타입을 암시적으로 var 키워드를 통해서 선언할 수 있음.
- 컴파일러에게 적절한 타입을 선택하도록 위임하여 성능상의 이득을 취할 수 있음.
- Queryable.
(ex. Queryable.Where)는 다수의 LINQ 쿼리문을 단일 SQL 쿼리로 합친 후 단번에 수행하지만, IEnumerable 는 LINQ 쿼리가 즉각 수행됨.
- Queryable.
Operator Overloading
- Operator Overloading
- access modifier는
public
과static
을 필요로 함.
Book book1 = new Book(234, 1);
Book book2 = new Book(234, 1);
var book3 = book1 + book2;
Console.WriteLine(book3.ToString()); // 468p. x 2
public class Book
{
private int _pagesCount;
private int _count;
public Book(int pagesCount, int count)
{
_pagesCount = pagesCount;
_count = count;
}
public override string ToString()
{
return $"{_pagesCount}p. x {_count}";
}
public static Book operator +(Book book1, Book book2)
{
return new Book(book1._pagesCount + book2._pagesCount, book1._count + book2._count);
}
}
Interface & Abstract
- Interface
- 내부 구조나 동작 방법을 공개하지 않고도 사용할 수 있도록 기능을 제공함.
- 따라서 내부 구조, 동작 방법을 변경하여도 사용자에게 영향을 주지 않을 수 있음.
- C#
- Class, Struct, Record는 interface를 복수개 implement할 수 있음.
implement
: interface 내부의 모든 member를 구현함.Implicitly implement
(암묵적 구현)Explicitly implement
(명시적 구현): 구현 시에 Access modifier를 명시하지 않으며 인터페이스로 캐스팅하여 접근 가능. private로 객체를 통해 접근이 불가함.- 하나의 메소드에 대해 암묵적 구현과 명시적 구현이 동시에 이뤄질 수 있음. 암묵적 구현만 된 경우에는 반드시 public이어야 함.
- virtual 메소드는 인터페이스 내부에서 구현되어야하며, Implementation Class에서 재정의할 때는 override 키워드를 사용하지 않음.
sample
Printer printer = new Printer();
printer.PrintMessage();
IPrinter iPrinter = printer;
iPrinter.PrintMessage();
interface IPrinter
{
void PrintMessage();
}
public class Printer : IPrinter
{
public void PrintMessage()
{
Console.WriteLine("Print Implicit Message");
}
void IPrinter.PrintMessage()
{
Console.WriteLine("Print Explicit Message");
}
}
output
Print Implicit Message
Print Explicit Message
상수
const
: 컴파일타임 상수readonly
: 런타임 상수- 컴파일타임 상수는 컴파일 후(IL), 값이 지정된 상수로 대체됨. 따라서 내장 자료형(숫자, 문자열, enum…)에만 사용 가능함.
- 런타임 상수는 런타임에 값이 평가됨. 상수에 대한 참조로 컴파일됨.
Static
- 정적 클래스
- Static Class은 인스턴스화 될 수 없음.
- 정적 생성자
- CLR은 특정 타입에 접근하는 경우, 정적 생성자를 우선으로 호출
- 정적 생성자는 access modifier가 허용되지 않음
Fruit fruit = new();
public class Fruit
{
public Fruit()
{
Console.WriteLine("Fruit()");
}
static Fruit()
{
Console.WriteLine("Static Fruit()");
}
}
// output
// Static Fruit()
// Fruit()
- 정적 멤버
- 생성된 인스턴스의 수와 관계없이 유일함.
- 비정적 클래스도 정적 멤버를 포함할 수 있음.
- 정적 메소드는 비정적 필드, 이벤트에 액세스 불가함.
- 클래스에만 속해있고 인스턴스에 속해있지는 않기 때문에, overloading만 가능하고 overriding은 불가함.
const
는 static 키워드를 함께 쓸 수 없지만, 정적으로 동작함.- 호출 -> 정적 멤버 초기화 -> 정적 생성자 호출(없으면 pass)
- 확장 메소드
- 확장 메소드는 추가하려는 기능이 타입에 포함되는 경우에 사용하는 것이 맞음.
using StringExtensions;
var targetString = "ah";
Console.WriteLine(targetString.GetRepeatedString(3)); // ahahah
namespace StringExtensions
{
public static class StringPrinter
{
public static string GetRepeatedString(this string str, int count)
{
StringBuilder sb = new();
for (int i = 0; i < count; i++)
sb.Append(str);
return sb.ToString();
}
}
}
Partial Class
- Partial Class 문서
- Class, Struct, Interface, Method의 정의를 나눌 수 있음.
- Method의 경우 Definition과 Implementation을 나눌 수 있음.
- static, unsafe 사용 불가함.
- 장점
- 큰 프로젝트에서 많은 사용자가 개별로 편집할 수 있음.
- 자동으로 생성된 소스로 작업할 시 파일 재생성 없이 코드 추가 가능.
Printer printer = new();
printer.PrintMainMessage();
printer.PrintSubMessage();
printer.PrintEndMessage();
public partial class Printer
{
private string _name = "YUREI";
private int _count = 0;
public void PrintMainMessage()
{
Console.WriteLine($"{_name}: Main Message - {_count}");
AddCount();
}
public partial void PrintEndMessage();
}
public partial class Printer
{
public int Count
{
get => _count;
}
public void PrintSubMessage()
{
Console.WriteLine($"{_name}: Sub Message - {_count}");
AddCount();
}
private void AddCount()
{
_count++;
}
public partial void PrintEndMessage()
{
Console.WriteLine($"{_name}: End Message - {_count}");
}
}
YUREI: Main Message - 0
YUREI: Sub Message - 1
YUREI: End Message - 2