본문 바로가기

개발 공부 유니티, C#/스터디

제프리 리처의 CLR via C# 3부 핵심 타입 part.1

14장 문자, 문자열, 텍스트 사용하기

숫자 → 문자 변환

 

캐스팅 : 가장 효율적,

int number;
char c = (char)number; //A=65 그런거

  
Convert : 데이터 소실 가능성이 있거나, 오버 플로우시 예외 발생

char c = Convert.ToChar(7000); //16비트로 표현할 수 없는 숫자. 예외발생

  
IConvertible : ToString, ToChar 등에서 구현 : 가장 비효율. 박싱이 발생



String


변경할 수 없는 문자들의 순서 (배열)
항상 힙에 할당

IComparable, IComparable<String>, ICloneable, IConvertible, IEnumerable, IEnumerable<Char>, IEquatable<String> 같은 다수의 인테페이스 구현

String s = "Hello"; // O
String s = new String("Hello"); // X

 

문자열은 변경이 불가능

새로운 문자로 할당하는 과정(SubString, ToUpper 등)을 겪는다면 새로 생성된 문자열을 대입. 기존에 존재하던 문자열을 할당해제 하게되므로 GC가 발생


https://tsyang.tistory.com/29

문자열을 비교

Equals, Compare, StartsWith, EndsWith사용

문자열 풀링 : 문자열 사용시 불필요한 문자열이 많아 질 수 있으므로
컴파일러들은 동일 리터럴 문자열을 모듈의 메타데이터에 단 한번만 기록 해당 문자열을 참조하는 모든 코드는 동일 문자열을 참조하도록 수정
여러 군데서 여러 string변수가 같은 문자열을 선언 했을때 낭비를 줄일 수 있음.
 

String 타입 복사 관련

Clone : 참조 반환

Copy : 복사본 반환

CopyTo : 문자열의 일부분을 배열로 반환

substring : 원래 문자열에서 일부분을 새로운 문자열을 반환

ToString : 현재 객체 참조(this)를 그대로 반환

객체에 대한 문자열 표현 얻어오기 : ToString
 
문자열을 객체로 분석하기 : Parse
int, DateTime 등에서 문자열 얻기
예제 코드 수록하기)
 


인코딩 : 문자 배열과 바이트 배열 사이의 변환


 
CLR에서는 모든 문자/문자열을 16비트 유니코드로 표현
인코딩 과정을 활용하면 유니코드를 지원하지 않는 시스템과도 문자열을 주고 받을 수 있다.
가장 대중적인 인코딩
UTF-16 : 리틀 엔디안 빅엔디안 사이 변환에도 쓰임, 압축도 발생하지 않아 성능상 가장 우수
UTF-8 : 글자 종류(언어)에 따라 1~4바이트로 인코딩 수행, 대중적이지만 특정크기 이상의 문자 값을 인코딩 할 경우 UTF-16에 비해 비효율
 
잘안씀/퇴물
UTF-32 UTF-7 ASCII
 
 
BOM 바이트 정렬기호
 
올바르지 않은 바이트 정렬 순서로 디코딩 하는 경우 예외를 발생 시킴
 
System.Security.SecureString을 사용하면 비관리 메모리 영역에 문자의 배열을 할당 
여긴 GC가 돌지 않음
민감한 정보보호 용이지만 사용시 문자열을 복호화, 다시 암호화하는 과정을 거치면서 아주 짧은 시간만 암호화되지 않은 상태로 유지되므로 성능이 현저히 느림



패스한 내용

*StringBuilder에 대한 것은 Effective C#에서도 여러번 언급해서 패스

*문자열 비교시 문자열 길이가 같은지 확인, 같은 문화권인지 확인하는 내용이 있는데, 문화권 체크는 잘 사용하고 있지 않아서 패스

 

문자열 인터닝 : 특정한 상황에서만 쓰이기 위함이라 유용하지는 않음.

 

 

15장 열거 타입과 비트 플래그

 

열거 타입 상속 구조

 

모든 열거타입 → System.Enum → System.ValueType → System.Object 

System.Enum은 코드에서 상속할 수 없다.

 

열거 타입은 값 타입이지만 C# 확장 메서드 기능을 이용해 메소드 사용을 흉내낼 수 있다.

컴파일러에 의해 숫자 값으로 치환된 이후 Enum값은 참조 관계가 남지 않는다.

자체 참조를 하는 아래와 같은 상황에서는 컴파일 이후에도 열거 타입에 대한 참조가 남게 된다.

 

 

//둘 다 열거 타입의 값을 저장하고 있는 Type을 반환
public static Type GetUnderlyingType(Type enumType); //System.Enum 타입에 정의
public static Type GetEnumUnderlyingType(); //System.Type에 정의

//ToString 많이 쓰지만 Enum.Format은 static이라 인스턴스를 만들지 않아도 되는 장점이 있다.
Console.WriteLine(Enum.Format(typeof(Color), (Byte)3, "G")); //Blue반환 

//수치 값에 대한 문자열 표현을 반환
public static String GetName(Type enumType, Object value);
public String GetEnumName(Object value);

//여기부터 위에 있는 System.Enum, System.Type에 정의된 메소드 2개 생략
Parse
TryParse

//대소문자를 구분함. Enum에 없는 값일 경우 ArgumentException 발생
Color c = (Color) Enum.Parse9typeof(Color), "orange", true);

//Color 열거 타입의 인스턴스를 만들고 숫자 값 1을 저장
Enum.TryParse<Color>("1", false, out c);

IsDefined

//Red가 1로 저장되어 있으므로 true 반환
Enum.IsDefined(typeof(Color), (Byte)1);

 

비트 플래그

System.IO.File 타입의 GetAttributes() 호출시 public enum FileAttributes 열거형 반환

FCL에 정의

→ 이거에 대한 문서 가져오기

//어떤 파일이 숨김 속성으로 지정 되었는지 확인
String file = Assembly.GetEntry Assembly().Location;
FileAttributes attributes = File.GetAttributes(file);

//파일을 읽기 전용이면서 숨김 속성으로 변경
File.SetAttributes(file, FileAttributes.ReadOnly | FileAttributes.Hidden);


열거형과 비트 플래그는 유사해 보이지만
열거형은 단일 수치값
비트 플래그는 특정 비트값의 설정 유무를 나타냄 (위의 경우 ReadOnly, Hidden이 모두 설정)

동작 방식

 

[Flags] // C#에서는 [FlagAttribute] 모두 허용
internal enum Actions
{
    None = 0,
    Read = 0x0001,
    Write = 0x0002, 
    ReadWrite = Actions.Read | Actions.Write,
    Delete = 0x0004,
    Query = 0x0008,
    Sync = 0x0010,
}


Read Delete속성을 타입에 ToString을 사용할 경우 1, 4 를 조합한 5가 반환됨 16진수에서 각자리수 마다 올라가는 방식이라 합쳤을때 겹치는 수는 없음(각 자리수를 true/false 형태로 씀)


열거 타입에 메서드 추가하기 by 확장 메서드

internal static class FileAttrubutesExtensionMethods
{
    public static Boolean IsSet(this FileAttrubutes flags, FileAttrubutes flagTo Test)
    {
	    if(flagToTest == 0)
   		{
    		throw new ArgumentOutOfRangeException("flagToTest", "Value must not be 0");
    		return (flags & flagToTest) == flagTest;
    	}
    }
}

FileAttributes fa = FileAttribtes.System;

if(fa.IsSet(FileAttrubutes.ReadOnly))
	Console.WriteLine("ReadOnly로 설정된 파일");



16장 배열

 

//배열의 각 요소를 초기화 하는 문법적 지원
String[] names = new String[] { "ydw", "KKK" };


System.Array를 모든 배열이 상속
IEnumerable, IConllection, IList 인터페이스를 모든 배열이 구현

p436 값타입 배열과 참조 타입의 배열이 관리되는 힙상에서의 할당방식 그림 넣기

배열 이니셜 라이저
//배열의 각 요소를 초기화 하는 문법적 지원
String[] names = new String[] { "ydw", "KKK" };

배열 캐스팅

//동일한 차원, 암묵적/명시적으로 변환이 가능해야함. 값타입으로 구성된 배열은 다른 타입으로 캐스팅 불가.
FileStream[,] fs2dim = new FileStream[5, 10];
Object[,] o2dim = fs2dim;


Array.Copy는 얕은 복사
깊은 복사를 위해서는 직접 구현 필요. foreach는 참조기 때문에 for문을 써서 구현

 


안전하지않은 배열

성능이 매우 중요한 경우. 배열 객체를 힙에 만들지 않고 스택에 만듬.
stackalloc문 사용, 값 타입으로만 만들 수 있고, 값 타입(구조체도)이 참조타입을 포함해서는 안됨,
필드 그자체또는 그 필드를 포함하는 구조체는 unsafe를 추가
배열 필드는 fixed 키워드로 표시
배열은 1차원, 시작인덱스가 0이어야함

스택에 할당되므로 메소드가 반환되면 즉시 벗어나 성능 우수

private static void StackallocDemo()
{
    unsafe 
    {
        const int width = 20;
        Char* pc = stackalloc Char[width]; //스택상에 메모리 할당.
        String s = "Jeffrey Richer";

        for(int index = 0; index < width, index++)
        {
	        pc[width - index - 1] = (index < s.Length) ? s[index] : '.';
        }
        Console.WriteLine(new String(pc, 0, width));
    }
}

private static void InlineArrayDemo()
{
    unsafe 
    {
        CharArray ca;
        Int widthInBytes = sizeof(CharArray);
        Int width = widthInBytes / 2; 

        String s = "Jeffrey Richer";

        for(int index = 0; index < width, index++)
        {
            ca.Characters[width - index - 1] = (index < s.Length) ? s[index] : '.';
        }
		Console.WriteLine(new String(ca.Characters, 0, width));
	}
}

internal unsafe struct CharArray
{
	//이 배열은 구조체에 직접 추가되는 배열이다.
	public fixed Char Characters[20];
}


--제외한 내용
//암묵적 타입에서 초기화시 컴파일러가 각 변수들을 검사하여 타입을 유추
//가능하지만.. 별로 좋아보이진 않음.
var names = new[] { "ydw", "asdf", null };

시작인덱스가 0이아닌 배열 만들기 -> 굳이