배열 / 정적 클래스 / 확장메서드
#define EXAMPLE_TYPE_ARRAY
#define EXAMPLE_TYPE_STATIC_CLASS // 많이 써라
#define EXAMPLE_TYPE_EXTEND_METHOD // 많이 써라
...
public class Example_02 : MonoBehavior
{
public void Awake
{
#if EXAMPLE_TYPE_ARRAY
//1. 평균적으로 사용하는 방법
int[] oValues = new int[5];
// 2. 1번보단 이걸 더 많이 쓰세요.
int[] oOtherValues = new int[]
{
1,2,3,4,5
};
var oStringBuilder = new System.Text.StringBuilder();
for(int i=0; i<oOtherValues.Length; i++)
{
//Append(): n1 문자열 뒤에 n2 문자열을 붙인다.(추가)
//AppendFormat(): 형식에 맞춘 문자열 추가(문자열 정렬)
oStringBuilder.AppendFormat("{0}". oOtherValues[i]);
}
Debug.Log(oStringBuilder.ToString());
}
#elif EXAMPLE_TYPE_STATIC_CLASS
#else EXAMPLE_TYPE_EXTEND_METHOD
#endif
}
}
C# 언어의 배열 특징
C#언어의 배열은 참조 형식의 데이터 타입에 속한다.
즉 가비지 컬렉션에 의해 메모리가 관리된다.
C# 언어의 배열은 가변 길이 배열을 지원한다.
즉 배열의 각 행의 길이를 서로 다르게 명시하는 것이 가능하다.
단 가변 길이 배열은 2차원 배열만 지원한다.
C++은 다차원 배열 지원, C#은 2차원 배열까지 지원.
왜 2차원 배열을 지원하는가? 행렬을 표현하기 위함!
정적 클래스
- 클래스의 멤버 변수, 함수가 모두 정적으로 이루어진 클래스를 의미한다.
정적 클래스는 객체화시키는 것이 불가능하다.
- 또한 정적 클래스는 정적 변수, 함수 이외에 멤버 변수, 함수 등을 선언하는 것이 불가능하며 상속 또한 불가능하다.
C# 언어 정적 클래스 특징
- C# 언어의 정적 클래스는 static 키워드를 명시함으로 정의하는 것이 가능.
static 키워드가 명시된 클래스는 클래스 변수, 함수 이외에는 다른 멤버를 정의하는 것이 불가능.
확장 메서드
- 기존에 제공되는 클래스 또는 외부 라이브러리에 속해 있는 클래스에 새로운 기능을 상속이 아닌 메서드를 이용해서 기능을 확장하는 것을 의미한다.
확장 메서드 특징
- C# 언어의 확장 메서드는 정적 클래스만을 통해서 정의하는 것이 가능하다.
즉, 정적 클래스가 아니면 확장 메서드 자체를 구현하는 것이 불가능하다.
- C# 언어의 확장 메서드를 정의하는 정적 클래스는 반드시 최상단 영역에 위치시켜야 한다.
- 특정 클래스의 중첩 클래스(클래스 안에 클래스 선언한 형태)로는 확장 메서드를 정의하는 것이 불가능하다.
var
auto와 비슷한 키워드.
C#의 형식 검사는 강한 검사와 약한 검사가 있음.
강한 형식 검사: int, float, string 등. 데이터 손실 및 프로그래머 실수 방지
약한 형식 검사: 자동으로 해당 변수의 형식 지정 (추정해줌) 단 지역 변수로 선언하고 선언과 동시에 초기화해줘야 한다.(컴퓨터는 모르니까 무슨 형식인지 유추 가능하도록)
String
읽기가 많은 경우는 string 클래스를 사용하는 게 더 적합하다. (데이터 불러올 때... 상점 등에 많이 씀)
단점. 느리다
StringBuilder
객체의 참조 값을 힙에서 관리한다. (추가, 삽입, 삭제 가능 - 동적 메모리)
새로운 객체를 만들지 않고 문자열을 수정할 수 있다.
장점. 빠르다.
단점. 메모리 사용량이 높다.
기존 + 연산자를 통한 문자열 연결은 메모리의 할당과 복사가 지속적으로 수행된다. (이건 모든 언어 공통 -> 느려짐)
반복분에서 많은 문자열을 연결하면 매우 느리다.
성능이 떨어지는 이유: 연결한 문자열 복사를 위해 매번 새로운 객체를 생성하기 때문
반면 StringBuilder는 메모리를 블럭 단위로 할당한 후 현재 블럭 내 메모리가 모두 소진된 경우에 한해 메모리를 재할당하게 된다. (빌더 쪽이 훨씬 빠르다!)
C# 언어의 2차원 이상의 배열은 ,를 통해 명시하는 것이 가능하다.
C/C++ 2차원 => [ ] [ ]
int[ , ] oMatrix = new int [ , ]
{
{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15},
{16,17,18,19,20}
};
arr.GetLength(0): 행의 개수
arr.GetLength(1): 열의 개수
STATIC CLASS
public static class CWidget
{
public static int m_nValue = 0;
public static string m_oString = "";
//초기화할 수 있는 함수
public static void Init(int a_nValue, string a_oString)
{
a_nValue = a_nValue;
a_oString = a_oString;
}
//값을 확인할 output 메소드 생성
public static void ShowInfo()
{
Debug.LogFormat("Value: {0}, String {1}, m_nValue, a_oString_;
}
}
동적 / 정적 차이점
1. 저장되는 영역이 다르다.
2. 값 유지시킬지 아닐지의 유무
//C#중급 수문장
#define EXAMPLE_TYPE_COLLECTION
#define EXAMPLE_TYPE_INDEXER
//C# 고급 수문장
#define EXAMPLE_TYPE_GENERIC
제네릭: C# 초심자인지 중급인지 구분해주는 문법임..!
코드 효율, 메모리 관리, 가독성 면에서 좋음.
C++: 템플릿에 종속되어 있음 / C#: 제네릭에 종속되어 있음
컬렉션
- 리스트, 사전과 같은 대량의 데이터를 관리 및 제어할 수 있는 클래스를 의미
배열 또한 컬렉션에 포함된다.
C#언어 컬렉션의 특징
- C# 언어의 컬렉션은 기본적으로 모든 타입의 데이터를 보관하는 것이 가능하다.
즉 object형을 기본으로 동작하도록 설계되어 있다. => 박싱/ 언박싱 발생 => 퍼포먼스 저하
인덱서
- C++의 인덱스 연산자 오버로딩과 같이 객체의 인덱스 연산을 시킬 때 특정 함수를 호출해주는 매커니즘을 의미. 즉 객체를 배열처럼 사용하는 것이 가능.
- 가독성 + 중복 로직 방지
C# 인덱서의 특징
- C# 언어의 인덱서는 this 키워드와 인덱스 연산자 ( [ ] )를 조합해서 정의하는 것이 가능.
- C# 언어의 인덱서는 this 키워드를 통해 정의되기 때문에 정적 클래스는 인덱서를 정의하는 것이 불가능.
- C# 언어의 인덱서는 프로퍼티와 마찬가지로 get과 set을 통해 로직을 정의하는 것이 가능.
-C# 언어의 인덱서는 매서드와 마찬가지로 virtual과 override키워드의 조합으로 오버라이드 매커니즘 구현 가능.
제네릭
- C++의 템플릿과 마찬가지로 클래스 또는 메서드를 정의할 때 타입을 지정하지 않고 구현할 수 있는 매커니즘.
제네릭의 특징
- C# 언어의 제네릭은 <T>와 같은 제네릭 타입을 명시함으로서 정의하는 것이 가능하다. ex. Class CWidget<T>
- C# 언어의 제네릭은 기본적으로 모든 데이터 타입에 동작하도록 설계되어야 한다.
- 제네릭 클래스 또는 메서드에 어떤 데이터 타입이 지정되어도 내부 로직에 변화가 발생하면 안된다.
=> 컴파일 오류 발생하면 좀 나은데.. 논리 오류 발생하면 피곤해진다. 설계할 때 방지해야!
C# 언어 제네릭의 데이터 타입 제한
- class CWidget<T> where T : class
=> 타입을 참조 형식으로 제한
- class CWidget <T> where T : struct
=> 타입을 값 형식으로 제한
- class CWidget <T> where T : SomeClass
=> 타입을 SomeClass를 직/간접적으로 상속하는 형식으로 제한
- class CWidget <T> where T : SomeInterface
=> 타입을 SomeInterface를 직/간접적으로 따르는 형식으로 제한
- class CWidget <T, U> where T : U
=> 타입을 U (클래스 or 인터페이스)를 직/간접적으로 상속하는 형식으로 제한
is와 as 연산자를 통한 데이터 형 변환
- is 연산자는 결과 값이 참/ 거짓으로 판정되며 값 형식과 참조 형식 데이터에 모두 사용하는 것이 가능.
- as 연산자는 결과를 해당 데이터 형의 참조 값으로 반환시키기 때문에 참조 형식의 데이터 타입에만 사용하는 것이 가능.
is 연산자
object oIntObject = 10;
if(oIntObject is int)
{
// !Do Nothing
}
as 연산자
object oClassObject = new SomeClass();
var SomeClass = oClassObject as SomeClass;
if(oSomeClass != null)
{
// !Do Nothing
}
※foreach 또는 이너머레이터 기반의 컬렉션 순회는 내부적으로 가비지 컬렉션을 유발하기 때문에 사능한 사용을 자제해야 한다. 즉 퍼포먼스가 별도로 필요 없는 로직에서만 제한적으로 사용한다.
(ex. 문자+문자열(파일 시스템->케바케) + 옵션값 세팅)(INI) => 주로 환경설정에 많이 사용되는 포맷
자료구조
.Net : HashTable
C++ : Map
C# : Dictionary
공통 특성: key와 Value를 사용해 자료를 저장하는 자료구조 => 빠르게 데이터를 검색할 수 있다.
Key값을 Hash함수에 넣어 코드 값으로 변경한 후 Bucket(배열)이라는 저장공간에 인덱스 번호를 맵핑시켜서 데이터를 저장한다.
해시 테이블은 각각의 Key 값에 해시 함수를 적용해 배열의 고유한 Index를 생성하고, 이 Index를 활용하여 값을 저장하거나 검색을 하는 곳에 사용이 된다.
여기서 실제 값이 저장되는 장소를 버킷 또는 슬롯이라 한다.
해시테이블 특징
1. 제네릭을 사용하지 않는다.
2. Key와 Value 모두 Object를 입력받는다.
3. 박싱과 언박싱을 사용한다.
4. 위에서 유추가 가능하듯 해시테이블은 자료형을 명시하지 않아도 된다.
※제네릭을 이용하지 않고 Object를 입력받기 때문에 모든 데이터 타입을 처리할 수 있다.
하지만 내부적으로 입력과 출력에 박싱과 언박싱이 발생한다.
딕셔너리(Dictionary)
- 딕셔너리 또한 Key와 Value를 사용해 자료를 저장하는 자료구조. => 빠르게 검색 가능
딕셔너리 특징(대부분 해시랑 반대임..)
1. 제네릭
2. Key와 Value 모두 Strong Type을 입력받는다.
※Strong Type: 앞에 어떤 타입인지 정확히 명시해주는 것. 선언 시 타입을 입력해줘야 한다는 뜻.
강력한 형식이라고 하며 서로 다른 데이터 타입을 사용할 수 있는 문법에 엄격한 제한을 두는 방식(오류 방지 -> C 계열 언어는 프로그래밍 언어 중 타입에 매우 민감)
3. 박싱, 언박싱 없음.
4. 선언 시 자료형을 명시해야 한다.
5. 자료형을 명시하기 때문에 형변환 리스크가 없다.
.Net의 HashTable과 Dictionary<T>의 특징
- 키를 가지고 빠르게 값을 접근하기 좋다.
★- 순서나 중복되는 데이터가 있는 경우에는 사용XXXXXXXXXX
- 미리 저장공간을 확보해 놓기 때문에 메모리 효율은 좋지 않음.
- 평균적으로 제네릭을 사용하고 연산이 빠른 Dictionary를 더 많이 사용하는 편
- 해시테이블보다 딕셔너리가 연산속도가 더 빠르다.(모든 타입을 받는지 아닌지에 대한 여부 차이)
결론
- 둘 다 사용법은 비슷하지만 내부적인 처리와 수용하는 타입의 형태가 다르므로 필요에 따라서 어느 걸 사용해야 할 지는 신중히 고민을 해봐야 한다.
해시 테이블 사용
- Value에 일정한 형식이 없고 여러 형태를 사용할 때
딕셔너리 사용
- 고정적으로 하나의 타입만 입력받을 때
※List와 ArrayList의 차이:
arrayList는 모든 타입을 받을 수 있음(박싱/언박싱 발생). 편한 대신 퍼포먼스 느림. 데이터가 적을 때에만 씀. 앞에다 타입을 명시해주는 게 좋음.
캐스팅 연산자
C: (Type) =>'강제' 형번환. 좋은 방식은 아님 -> 오류 발생 가능성 매우 높음.
단 프로그래머가 명확하게 이해하고 작성 시 문제x
C++: ??
C#: 대표적인 형변환 3총사(Convert, Parse, TryParse)
Convert: 기본 데이터 형식을 다른 기본 데이터 형식으로 변환
기본 데이터 형식? : int, byte, char, bool 등...
Parse: 문자열을 해당하는 형식으로 변환시킨다.
TryParse: 문자열을 해당하는 형식으로 변환시키지만 성공 여부를 반환한다.(true/false)
특징: 체크해야 할 String값이 숫자인 경우 int타입으로 컨버팅해서 반환
체크해야 할 String 값이 숫자가 아닌 경우 설정한 기본값으로 반환
확장 메서드: 중복 로직 피하기 위해 사용.
제네릭이 필요한 이유+확장메서드의 강력함(전역 접근을 통해 불필요한 상속 싹 날려버림)
object oSumValue = oArrayList.GetSumValue<int>();
Debug.LogFormat("SumValue : {0}", (int)oSumValue);
오버로딩 => 중복정의 => 연산자 오버로딩
오버라이딩=>재정의
String -> 느리다. -> 안에 있는 길이를 측정할 수 있는 방법이 없음.
'이론(면접)' 카테고리의 다른 글
Attribute (0) | 2022.07.01 |
---|---|
SendMessage (ray) (0) | 2022.07.01 |
변수 표기법 (0) | 2022.06.30 |
[C#] $ (문자열 보간), Nullable (0) | 2022.06.29 |
C# 구조 (0) | 2022.06.28 |