C# 기초 문법 - 클래스,상속,델리게이트,열거형(2주차 3일)
정의가 가장 헷갈리는 부분이라 몇 번이고 다시 돌려보았다.
결국 모든 정의 들을 내가 알아볼 수 있도록 강의 자료를 참고해 정리하기로 했다...
📕문법강의-3주 차
1) 클래스와 객체
2) 상속과 다형성
3) 고급문법 및 기능
클래스(붕어빵틀)
객체를 생성하기 위한 템플릿 또는 설계도 역할을 함
속성과 동작을 가지고, 속성은 필드, 동작은 메서드로 표현함
객체를 생성하기 위해서는 클래스를 사용하여 인스턴스를 만들어야 한다.
👀클래스의 특징
1. 캡슐화 - 정보은닉, 외부 직접접근 제한, 안정성과 유지보수성
2. 상속 - 기존클래스를 확장하여 새로운 클래스를 만드는 메커니즘, 코드 중복을 줄임
3. 다형성 - 오버로딩과 오버라이딩을 통해 구현가능
4. 추상화 - 복잡한 시스템, 개념 단순화
5. 객체(Object) - 클래스로부터 생성된 실체.
객체들 간의 상호작용을 통해 프로그램 동작, 모듈화와 재사용성을 높임
👀 클래스의 구성요소
필드 - 변수
메서드 - 동작정의(함수)
생성자 - 객체를 초기화하는 역할, 객체가 생성될 때 자동호출
소멸자 - 객체가 소멸될 때 호출 되는 메서드, 메모리나 리소스의 해제 등의 작업 수행
객체(붕어빵)
클래스의 인스턴스이다. 클래스의 실체화된 형태라고 할 수 있다.
객체는 클래스로부터 생성되고, 각 객체는 독립적인 상태, 고유한 데이터를 가질 수 있다.
👀구조체 VS클래스
- 구조체와 클래스는 모두 사용자 정의 형식을 만드는 데 사용될 수 있다.
- 구조체는 값 형식, 스택영역에 할당되고, 복사될 때 값이 복사됨.
- 클래스는 참조 형식, 힙(동적할당)영역에 할 당 되고 참조로 전달됨 성능에 다소 차이가 있음
- 구조체는 상속을 받을 수 없지만, 클래스는 단일 상속 및 다중 상속이 가능하다.
- 구조체는 작은 크기의 데이터 저장이나 단순한 데이터 구조에 적합.
- 클래스는 더 복잡한 객체를 표현하고, 다양한 기능을 제공하기 위해 사용
생성자와 소멸자(메서드)
생성자는 클래스의 인스턴스를 생성할 때 호출되는 메서드
소멸자는 클래스의 인스턴스가 메모리에서 해제될 때 호출되는 메서드
생성자
생성자는 객체가 생성될 때 호출되는 특별한 메서드
클래스의 인스턴스(객체)를 초기화하고, 필요한 초기값을 설정하는 역할을 수행
생성자는 클래스와 동일한 이름을 가지며, 반환 타입이 없음
객체를 생성할 때 new 키워드와 함께 호출
👀 생성자의 특징
- 객체를 초기화하는 과정에서 필요한 작업을 수행할 수 있음
- 생성자는 여러 개 정의할 수 있으며, 매개변수의 개수와 타입에 따라 다른 생성자를 호출할 수 있음
이를 생성자 오버로딩이라고 합니다.
- 기본적으로 매개변수가 없는 디폴트 생성자가 클래스에 자동으로 생성되지만,
사용자가 직접 정의한 생성자가 있는 경우 디폴트 생성자가 자동으로 생성되지 않습니다
소멸자
소멸자는 객체가 소멸되는 시점에서 자동으로 호출되는 특별한 메서드
객체의 사용이 종료되고 메모리에서 해제될 때 자동으로 호출되어 필요한 정리 작업을 수행
클래스와 동일한 이름을 가지며, 이름 앞에 ~ 기호를 붙여 표현
소멸자는 반환 타입이 없고 매개변수를 가질 수 없음
C#에서는 가비지 컬렉터(Garbage Collector)에 의해 관리되는 메모리 해제를 담당하므로,
명시적으로 소멸자를 호출하는 것은 일반적으로 권장되지 않음
👀 소멸자의 역할
- 자원 해제: 파일 핸들, 네트워크 연결, 데이터베이스 연결 등의 외부 리소스를 사용한 경우, 소멸자를 통해 해당 리소스를 해제할 수 있음
- 메모리 해제: 객체가 사용한 메모리를 해제하고 관련된 자원을 정리할 수 있음
- 로깅 및 디버깅: 객체가 소멸되는 시점에 로깅 작업을 수행하거나 디버깅 정보를 기록할 수 있음
프로퍼티
프로퍼티는 클래스 멤버로서, 객체의 필드 값을 읽거나 설정하는 데 사용되는 접근자(Accessor) 메서드의 조합
객체의 필드에 직접 접근하지 않고, 간접적으로 값을 설정하거나 읽을 수 있도록 함
필드에 대한 접근 제어와 데이터 유효성 검사 등을 수행할 수 있음
프로퍼티는 필드와 마찬가지로 객체의 상태를 나타내는 데이터 역할을 하지만, 외부에서 접근할 때 추가적인 로직을 수행할 수 있음
👀 프로퍼티 구문
프로퍼티는 get과 set 접근자를 사용하여 값을 읽고 설정하는 동작을 정의
get 접근자는 프로퍼티의 값을 반환하고, set 접근자는 프로퍼티의 값을 설정
필요에 따라 get 또는 set 접근자 중 하나를 생략하여 읽기 전용 또는 쓰기 전용 프로퍼티를 정의할 수 있음
class Person
{
private string name;
private int age;
public string Name
{
get { return name; }
set { name = value; }
}
public int Age
{
get { return age; }
set { age = value; }
}
}
👀 자동프로퍼티
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
상속
객체지향 프로그래밍에서 중요한 개념
개념
상속은 기존의 클래스(부모 클래스 또는 상위 클래스)를 확장하거나 재사용하여
새로운 클래스(자식 클래스 또는 하위 클래스)를 생성하는 것
자식 클래스는 부모 클래스의 멤버(필드, 메서드, 프로퍼티 등)를 상속받아 사용할 수 있다
상속을 통해 부모 클래스의 기능을 확장하거나 수정하여 새로운 클래스를 정의할 수 있다
장점
코드의 재사용성 : 상속을 통해 기존 클래스의 코드를 재사용할 수 있으므로, 반복적인 코드 작성을 줄일 수 있다
계층 구조의 표현 : 클래스 간의 계층 구조를 표현하여 코드의 구조를 명확하게 표현할 수 있다
유지보수성의 향상 : 상속을 통해 기존 클래스의 수정이 필요한 경우, 해당 클래스만 수정하면 된다
상속의 종류
단일 상속 : 하나의 자식 클래스가 하나의 부모 클래스만 상속받는 것을 말함
C#에서는 단일 상속만을 지원함
다중 상속 : 하나의 자식 클래스가 여러 개의 부모 클래스를 동시에 상속받는 것을 말함.
C#은 다중 상속을 지원하지 않는다
인터페이스 상속 : 클래스가 인터페이스를 상속받는 것을 말함
인터페이스는 다중 상속을 지원
클래스는 하나의 클래스와 여러 개의 인터페이스를 동시에 상속받을 수 있다
상속의 특징
부모 클래스의 멤버에 접근 : 자식 클래스는 상속받은 부모 클래스의 멤버에 접근할 수 있으며, 이를 통해 부모 클래스의 기능을 재사용할 수 있음
메서드 재정의 : 자식 클래스는 부모 클래스의 메서드를 재정의하여 자신에게 맞게 수정할 수 있음
이를 통해 다형성(Polymorphism)을 구현할 수 있음
상속의 깊이 : 클래스는 다수의 계층적인 상속 구조를 가질 수 있음
부모 클래스가 또 다른 클래스의 자식 클래스가 될 수 있으며, 이를 통해 상속의 계층 구조를 형성할 수 있음
상속의 깊이가 깊어질수록 클래스 간의 관계가 복잡해질 수 있으므로,
적절한 상속의 깊이를 유지하고 상속을 적절하게 사용하는 것이 중요하다
접근 제한자와 상속 : 상속 관계에서 멤버의 접근 제한자는 중요한 역할 한다
부모 클래스의 멤버의 접근 제한자에 따라 자식 클래스에서 해당 멤버에 접근할 수 있는 범위가 결정된다
접근 제한자를 통해 상속된 멤버의 가시성을 조절하여 캡슐화와 정보 은닉을 구현할 수 있다
다형성
같은 타입이지만 다양한 동작을 수행할 수 있는 능력
가상(Virtual) 메서드
필요에 따라 재정의를 하거나 확장할 수 있음
override로 재정의를 해줘야 override의 내용이 출력됨
virtual로 생성해도 override로 재정의를 해주지 않으면, virtual의 메서드가 실행됨
가상메서드로 생성하지 않았을 때도 리스트 출력 등의 경우 위와 동일하게 작동하게 됨
추상(Abstract) 클래스와 메서드
추상클래스는 직접적으로 인스턴스를 생성할 수 없는 클래스상속을 위한 베이스 클래스로 사용추상클래스는 abstruct 키워드를 사용하여 선언되고, 추상메서드를 포함할 수 있음 추상메서드는 구현부가 없는 메서드, 자식클래스에서 반드시 구현필요(override로 구현)
abstract class Shape
{
public abstract void Draw();
}
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
class Square : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a square");
}
}
class Triangle : Shape
{
public override void Draw()
{
Console.WriteLine("Drawing a triangle");
}
}
제너릭
제너릭은 클래스나 메서드를 일반화시켜 다양한 자료형에 대응할 수 있는 기능이다
제너릭을 사용하면 코드의 재사용성을 높일 수 있다
C#에서는 <T> 형태의 키워드를 이용하여 제너릭을 선언
제너릭 클래스나 메서드에서 사용할 자료형은 선언 시점이 아닌 사용 시점에 결정됨
제너릭 클래스나 메서드를 사용할 때는 <T> 대신 구체적인 자료형을 넣어준다
// 제너릭 클래스 선언 예시
class Stack<T>
{
private T[] elements;
private int top;
public Stack()
{
elements = new T[100];
top = 0;
}
public void Push(T item)
{
elements[top++] = item;
}
public T Pop()
{
return elements[--top];
}
}
// 제너릭 클래스 사용 예시
Stack<int> intStack = new Stack<int>();
intStack.Push(1);
intStack.Push(2);
intStack.Push(3);
Console.WriteLine(intStack.Pop()); // 출력 결과: 3
두 개 이상의 제네릭을 사용
class Pair<T1, T2>
{
public T1 First { get; set; }
public T2 Second { get; set; }
public Pair(T1 first, T2 second)
{
First = first;
Second = second;
}
public void Display()
{
Console.WriteLine($"First: {First}, Second: {Second}");
}
}
Pair<int, string> pair1 = new Pair<int, string>(1, "One");
pair1.Display();
Pair<double, bool> pair2 = new Pair<double, bool>(3.14, true);
pair2.Display();
#출력
First: 1, Second: One
First: 3.14, Second: True
out, ref 키워드
👀 사용법
- out, ref 키워드는 메서드에서 매개변수를 전달할 때 사용
- out 키워드는 메서드에서 반환 값을 매개변수로 전달하는 경우에 사용
- out 키워드는 항상 값을 채워줘야 한다
- ref 키워드는 메서드에서 매개변수를 수정하여 원래 값에 영향을 주는 경우에 사용
- ref 키워드는 그안에 값을 사용할지 안할지 모름
- out, ref 키워드를 사용하면 메서드에서 값을 반환하는 것이 아니라, 매개변수를 이용하여 값 전달가능
// out 키워드 사용 예시
void Divide(int a, int b, out int quotient, out int remainder)
{
quotient = a / b;
remainder = a % b;
}
int quotient, remainder;
Divide(7, 3, out quotient, out remainder);
Console.WriteLine($"{quotient}, {remainder}"); // 출력 결과: 2, 1
// ref 키워드 사용 예시
void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y);
Console.WriteLine($"{x}, {y}"); // 출력 결과: 2, 1
👀 주의 사항
값의 변경 가능성 : ref 매개변수를 사용하면 메서드 내에서 해당 변수의 값을 직접 변경가능
예기치 않은 동작을 초래할 수 있으므로 주의가 필요
성능 이슈 : ref 매개변수는 값에 대한 복사 없이 메서드 내에서 직접 접근할 수 있기 때문에 성능상 이점이 있다
그러나 너무 많은 매개변수를 ref로 전달하면 코드의 가독성이 떨어지고 유지보수가 어려워질 수 있다
적절한 상황에서 ref를 사용하는 것이 좋다
변수 변경 여부 주의 : out 매개변수는 메서드 내에서 반드시 값을 할당해야 한다
따라서 out 매개변수를 전달할 때 해당 변수의 이전 값이 유지되지 않으므로 주의필요
📕문법강의-4주 차
1) 인터페이스와 열거형
2) 예외처리 및 값형과 참조형
3) 델리게이트(Delegate), 람다(Lambda), Func과 Action, LINQ(Language Integrated Query)
4) 고급 자료형 및 기능( Nullable 형, 문자열 빌더 (StringBuilder))
인터페이스
인터페이스를 상속받는 클래스는 인터페이스 내의 메서드를 구현해야만 한다.
인터페이스를 매개변수로도 활용 할 수 있다
// 아이템을 사용할 수 있는 인터페이스
public interface IUsable
{
void Use();
}
// 아이템 클래스
public class Item : IUsable
{
public string Name { get; set; }
public void Use()
{
Console.WriteLine("아이템 {0}을 사용했습니다.", Name);
}
}
// 플레이어 클래스
public class Player
{
public void UseItem(IUsable item)
{
item.Use();
}
}
// 게임 실행
static void Main()
{
Player player = new Player();
Item item = new Item { Name = "Health Potion" };
player.UseItem(item);
}
델리게이트(Delegate)
공격콜백 받기 - event를 붙여서 사용
event는 할당연산자(=)를 사용할 수 없으며, (+= / -= 만 사용가능)
클래스 외부에서는 직접이벤트 호출할 수 없다.
?. 조건부 실행 null이 아니면, 실행
null이면 실행하지 않는다.
// 델리게이트 선언
public delegate void EnemyAttackHandler(float damage);
// 적 클래스
public class Enemy
{
// 공격 이벤트
public event EnemyAttackHandler OnAttack;
// 적의 공격 메서드
public void Attack(float damage)
{
// 이벤트 호출
OnAttack?.Invoke(damage);
// null 조건부 연산자
// null 참조가 아닌 경우에만 멤버에 접근하거나 메서드를 호출
}
}
// 플레이어 클래스
public class Player
{
// 플레이어가 받은 데미지 처리 메서드
public void HandleDamage(float damage)
{
// 플레이어의 체력 감소 등의 처리 로직
Console.WriteLine("플레이어가 {0}의 데미지를 입었습니다.", damage);
}
}
// 게임 실행
static void Main()
{
// 적 객체 생성
Enemy enemy = new Enemy();
// 플레이어 객체 생성
Player player = new Player();
// 플레이어의 데미지 처리 메서드를 적의 공격 이벤트에 추가
enemy.OnAttack += player.HandleDamage;
// 적의 공격
enemy.Attack(10.0f);
}
람다(Lambda)
익명 메서드를 만들 수 있다.
람다는 델리게이트를 사용하여 변수에 할당하거나,
메서드의 매개변수로 전달할 수 있다.
👀 람다 구조
Calculate calc = (x, y) =>
{
return x + y;
};
Calculate calc = (x, y) => x + y; //한줄 또는 리턴값만 있을 경우 중괄호 생략가능
다시 호출이 불가능하기 때문에 간단하게 실행할 경우에만 사용한다.
Func과 Action
Func과 Action은 델리게이트를 대체하는 미리 정의된 제네릭 형식이다.
Func는 값을 반환하는 메서드를 나타내는 델리게이트
ex) Func <int, string>은 int를 입력받고, string을 반환하는 메서드
Action은 값을 반환하지 않는 메서드를 나타내는 델리게이트
ex) Action <int, string>은 int와 string을 입렵받고, 아무 값도 반환하지 않음.
Func과 Actiond은 매개변수와 반환 타입을 간결하게 표현할 수 있다.
메서드를 저장해 놓는 기능! 유니티에서도 굉장히 많이 사용된다.
LINQ(Language Integrated Query)
. NET 프레임 워크에서 제공되는 쿼리 언어 확장
코드에서 쿼리 언어를 사용할 수 있다
데이터가 뭉쳐 있는 데이터 자료들에게 쿼리 언어를 사용.
데이터를 필터링, 정렬, 그룹화, 조인 등 다양한 작업을 수행
👀 LINQ구조( foreach문과 유사함 )
var result = from 변수 in 데이터소스
[where 조건식]
[orderby 정렬식 [, 정렬식...]]
[select 식];
var 키워드는 결과 값의 자료형을 자동으로 추론
from 절에서는 데이터 소스를 지정
where 절은 선택적으로 사용하며, 조건식을 지정하여 데이터를 필터링
orderby 절은 선택적으로 사용하며, 정렬 방식을 지정
select 절은 선택적으로 사용하며, 조회할 데이터를 지정
Nullable 형
Null = 아무것도 없음을 의미한다 (참조를 하지 않았다)
기본적으로 값형은 Null을 허용하지 않는다.
Nullable = 값형에 대한 특별한 형식
값형 변수에 null값을 지정할 수 있는 방법을 제공
형식은 ? 연산자를 사용하여 선언
문자열 빌더 (StringBuilder)
문자열조작
문자열들을 미리 받아 놓고
내부적인 버퍼(buffer)에 넣어놓고 필요할 때 문자열로 만들어줌.
Append(), Insert(), Replace(), Remove() 메서드를 사용해 추가, 삽입, 치환, 삭제 가능
가변성 - 문자열 조작가능
효율적인 메모리 관리
+ 연산자로 출력했을 경우 메모리에 저장을 해둠
👀 주요 메서드
Append : 문자열을 뒤에 추가합니다.
Insert : 문자열을 지정한 위치에 삽입합니다.
Remove : 지정한 위치에서 문자열을 제거합니다.
Replace : 문자열의 일부를 다른 문자열로 대체합니다.
Clear : StringBuilder의 내용을 모두 지웁니다.
사용예제
StringBuilder sb = new StringBuilder();
// 문자열 추가
sb.Append("Hello");
sb.Append(" ");
sb.Append("World");
// 문자열 삽입
sb.Insert(5, ", ");
// 문자열 치환
sb.Replace("World", "C#");
// 문자열 삭제
sb.Remove(5, 2);
// 완성된 문자열 출력
string result = sb.ToString();
Console.WriteLine(result);
📕문법강의 - 5주차
알고리즘 기초
정렬
탐색
고급