C# 종합 문법 4 - 1

김정환·2024년 9월 20일
0

인터페이스와 열거형

다중 상속을 쓰지 않는 이유

  1. 다이아몬드 문제
    다중 상속 시, 한 클래스에서 두 개 이상의 부모 클래스로부터 동일한 멤버를 상속한 경우.
    동일한 멤버는 어떤 부모 클래스의 멤버를 써야할 지 모호해진다.
  2. 설계의 복잡성
    관계가 복잡해진다.
  3. 이름 충돌과 충돌 해결의 어려움
    어느 순간 이름이 중첩되어 충돌을 일으킨다.
  4. 설계의 일관성과 단순성을 유지하기 위함
    C#은 단일 상속으로 설계의 단순성과 일관성을 유지

인터페이스를 쓰는 이유

  1. 코드의 재사용성
  2. 다중 상속 제공
  3. 유연한 설계
    클래스와 인터페이스 간 느슨한 결합

인터페이스 Interface

  • 현실 예시 : USB A 타입 포트
    입구와 형태만 맞으면 다양한 곳에서 호환되어 쓸 수 있음

특징

  • 클래스가 구현하는 멤버를 정의
  • 클래스에 대한 제약 조건 명시
  • 클래스에서 구현할 경우 인터페이스에 선언된 멤버들을 모두 정의해야함.

구현

Interface IMyInterface{ // 인터페이스명 앞에 I를 붙이는 것이 특징
	public void Method1();
}

class ClassA : IMyInterface{
	public void Method1(){
    	// 구현하지 않으면 에러 발생
    }
}
// 독특한 예시
Item item = new Item { Name = "Health Potion" };
// 선언하면서 이렇게도 작성할 수 있다.

다중 상속

Interface IUsable{
	void Use();
}

Interface IDestoryable{
	void Destory();
}

class Item : IUsable, IDestroyable
{
	public void Use(){
    
    }
    public void Destory(){
    
    }
}

인터페이스 vs 추상클래스

인터페이스

  • 추상적인 동작만 정의
  • 다중 상속 가능

추상클래스

  • 일부 동작의 구현 가능 + 추상 메서드를 포함할 수 있음.
  • 단일 상속만 가능

열거형

사용 이유

  1. 가독성
    연관된 상수들을 명명할 수 있음
  2. 자기 문서화
    의미 있는 이름을 사용할 수 있음
  3. 스위치문과 호환성
    스위치문과 쓸 때 깔끔하게 쓸 수 있다.

특징

  • 서로 관련된 상수(int)들의 집합을 정의할 때 사용
enum MyEnum {
	val1, 	// 디폴트 0
    val2, 	// 디폴트 1
    va13	// 디폴트3
}

// 임의 지정할 경우
enum MyEnum {
	val1 = 10, 	// 10
    val2, 		// 따로 지정하지않으면 앞선 변수 + 1 = 11
    va13,		// 12
    val4 = 100, // 100
}

열거형 형변환

  • 명시적 형변환으로 사용할 수 있음.
int intVal = (int)MyEnum.val1;
MyEnum eVal = (MyEnum)intVal;

스위치문 사용

switch(eVal){
	case MyEnum.val1:
    	break;
    case MyEnum.val2:
    	break;
    ...
}

예외 처리 및 값형 vs 참조형

예외

  • 실행 중 예기치 못한, 예상하지 못한 상황
  • 치명적인 오류의 원인 중 하나

예외 처리

  • 예외 상황에 대비하여 안정적으로 유지하기 위한 작업
  • 예외 처리로 안정성과 디버깅을 용이하게 함

구현

  • try-catch 문
try
{
	// 예외가 발생할 수 있는 코드
}
catch(ExceptionType1 ex)
{
	// 예외 타입 1에 해당하는 예외 처리
}
catch(ExceptionType2 ex)
{
	// 예외 타입 2에 해당하는 예외 처리
}
finally
{
	// 예외 발생 여부와 상관없이 항상 실행되는 코드
}

catch

  • 예외 발생 했을 때, 탐지 시 들어오는 구문
  • 여러 예외에 대한 구문을 배치할 수 있음
  • catch 블록은 순서대로 실행
  • 예외에 해당하는 첫번째 catch 블록이 먼저 실행
  • 여러 catch 블록을 써서 다양한 예외 타입 처리 가능
  • 예외 객체
    * catch문에서 예외 객체를 이용해서 예외에 대한 정보에 접근 가능

finally

  • 예외가 실행했든 아니든 무조건 실행
  • 예외 처리의 마지막 단계, 정리 단계
  • 생략 가능

실행 시점

  • 예외가 발생한 경우 : 예외 처리 과정 후 finally 블록 실행
  • 예외가 발생하지 않은 경우 : 발생하지 않아도 finally 블록 실행

사용자 정의 예외

  • 제공하는 예외 외에 사용자 자신만의 예외 클래스 작성 가능
  • Exception 클래스 상속으로 사용 가능
  • catch 문에 넣어서 사용

구현

try{
	int result = 10 / 0; // zero divide
    
}
catch(DivideByZeroException ex){
	//~
}

사용자 정의 예외 객체

class CustomException : Exception
{
	// 생성자
	public CustomException(string message) : base(message) // initializer
    {
    	
    }
}
  • initializer : 생성자가 실행되기 전에 처리할 것들을 명시
  • base = 부모클래스
  • base(message) : 부모 클래스 생성자를 먼저 호출하는 것

주의점

  • 예외 처리 작성 시, 너무 많은 구문을 try에 넣으면 오히려 처리하기 힘들어짐.
  • 예외가 일어날 것이라 우려되는 부분만 try에 넣기

값형 vs 참조형

값형 Value Type

  • struct, 일반 변수(int, float, double, bool 등등)
  • 변수값을 직접 저장
  • 변수가 실제 데이터를 보유, 다른 변수에 할당하거나 전달할 때는 값이 복사
struct MyStruct{
	public int val;
}

MyStruct s1;
s1.val = 10;

MyStruct s2 = s1; // s2에 s1의 값을 복사
s2.val = 20;

Console.WriteLine(s1.val); // 10

참조형 Reference Type

  • class, 배열, 인터페이스
  • 참조형만 기억하면 편함
  • 변수가 데이터에 대한 참조(메모리 주소) 를 저장
  • 변수가 실제 데이터를 가리키는 참조(포인터) 를 갖고 있어, 다른 변수에 할당할 때 참조 복사가 이루어짐
class MyClass{
	public int val;
}

MyClass obj1 = new MyClass();
obj1.val = 10;

MyClass obj2 = obj1;
obj2.val = 20;

Console.WriteLine(obj1.val); // 20

값형 vs 참조형

변수 저장 방법

  • 값형 : 실제 데이터를 변수에 저장
  • 참조형 : 데이터에 대한 참조를 변수에 저장

변수 할당 및 복사

  • 값형 : 변수 간 값 복사
  • 참조형 : 변수 간 참조 복사

데이터 보유 방법

  • 값형 : 변수가 독립적으로 데이터 보유
  • 참조형 : 변수가 동일한 데이터를 참조

박싱, 언박싱

박싱 Boxing

  • 값형 -> 참조형으로
  • 값형 변수 값을 메모리의 힙 영역에 할당된 객체로 랩핑(Wrapping)

언박싱 Unboxing

  • 참조형 -> 값형으로
  • 박싱된 객체에서 값을 추출해서 값형 변수에 할당

박싱, 언박싱 주요 특징

  • 값형 - 참조형 간 변환 작업. 작업 과정에서 메모리 소모가 크다.
    (값형에 변경되는 게 아니라 값형은 그대로 있고 새로운 박싱 객체에 값을 넣어주는 것)
  • 박싱된 객체는 힙 영역에 할당. 가비지 컬렉션의 대상이 될 수 있다.
    메모리 관리에 유의해야한다.
  • 박싱된 객체와 원래의 값형은 서로 독립적.
    값을 수정해도 상호간 영향을 주지 않는다.

예시

  • object 객체 :
    .net에서 제공하는 공통 자료형의 일부.
    모든 클래스의 직간접적인 상위 클래스.
// 박싱과 언박싱
int num1 = 10;
object obj = num1; // 박싱 : 값 -> 참조
int num2 = (int)obj; // 언박싱 : 참조 -> 값
Console.WriteLine("num1: " + num1); // 출력 결과: 10
Console.WriteLine("num2: " + num2); // 출력 결과: 10
  • 리스트 활용 예시
List<object> myList = new List<object>();

// 박싱: 값 형식을 참조 형식으로 변환하여 리스트에 추가
int intValue = 10;
myList.Add(intValue); // int를 object로 박싱하여 추가

float floatValue = 3.14f;
myList.Add(floatValue); // float를 object로 박싱하여 추가

// 언박싱: 참조 형식을 값 형식으로 변환하여 사용
int value1 = (int)myList[0]; // object를 int로 언박싱
float value2 = (float)myList[1]; // object를 float로 언박싱
  • 빈번한 사용은 성능 저하의 원인

tip

  • 예외 처리 시, 가능한 구체적인 예외 클래스 사용해야 좋음.
    코드가 더 안정적이고 예외 상황에 대한 처리가 더욱 명확해짐.
  • 값형과 참조형, 박싱과 언박싱의 개념을 확실히 이해야한다.
    C# 개발에서 매우 중요한 개념
profile
만성피로 개발자

0개의 댓글