Section 22.4: 비트 논리 연산 (bitwise logic) 을 통해 플래그 스타일의 enum 값 검사하기

플래그 스타일의 enum 값은 어느 특정 값 하나로 부합 여부를 확인할 수 없는 경우가 있으므로, 비트 논리 연산 (bitwise logic) 을 통해 검사를 수행하여야 한다.

[Flags] enum FlagsEnum { Option1 = 1, Option2 = 2, Option3 = 4, Option2And3 = Option2 | Option3, Default = Option1 | Option3, }

Default 라는 값은 실제적으로 두개의 다른 값들이 비트 논리연산자 OR 을 통해 결합된 값이다. 그러므로 특정 플래그가 설정되어 있는지를 확인하기 위해서는 비트 논리 연산자 AND 를 이용하여야 한다.

var value = FlagsEnum.Default; bool isOption2And3Set = (value & FlagsEnum.Option2And3) == FlagsEnum.Option2And3; Console.WriteLine(isOption2And3Set);

출력 결과 :

False
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 22.3: 플래그 선언을 위한 << 표기법

플래그를 위한 enum 을 선언하는 경우, 각 플래그 값들로 하여금 이진수로 표현 시 단 하나의 1 값만 존재하여야 하는 플래그 특성에 부합함을 보장하기 위해 좌측 시프트 연산자 (<<) 를 사용할 수 있다. 이는 많은 수의 플래그들이 선언된 큰 크기의 enum 에 대해서도 높은 가독성을 보장하는 데에 도움을 줄 수 있다.

[Flags] public enum MyEnum { None = 0, Flag1 = 1 << 0, Flag2 = 1 << 1, Flag3 = 1 << 2, Flag4 = 1 << 3, Flag5 = 1 << 4, ... Flag31 = 1 << 30 }

위의 예제에서 확인할 수 있듯이, MyEnum 이 적절한 플래그들을 설정하고 있음을 명확히 할 수 있으며 Flag30 = 1073741822 (혹은 이진수 111111111111111111111111111110) 와 같이 실제 사용에 적합하지 않은 혼란스러운 코드를 포함하지 않도록 할 수 있다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 22.2: Enum 값을 플래그 (flag) 로 사용하기

FlagsAttribute 를 사용하면 ToString() 메소드가 매칭되는 enum 값에 대해 동작하는 방식을 변경할 수 있다:

[Flags] enum MyEnum { // None = 0, 와 같은 값을 선언할 수는 있으나 비트 단위 (bitwise) 로 조합되어 사용될 수는 없다 FlagA = 1, FlagB = 2, FlagC = 4, FlagD = 8 // 비트 단위 (bitwise) 연산을 하용하기 위해서는 // 2 의 제곱수나 2의 제곱수의 조합을 사용해야 한다 } var twoFlags = MyEnum.FlagA | MyEnum.FlagB; // 이는 해당 변수가 갖는 모든 플래그들을 열거해 줄 것이다: "FlagA, FlagB". Console.WriteLine(twoFlags);

FlagsAttribute 속성은 열거형 상수들이 2의 제곱수 (혹은 2의 제곱수들의 조합) 이어야 하는 제약 조건이 있으며 enum 값은 기본적으로 숫자 형식이기 때문에, 내부 숫자 형식 타입의 크기에 제약을 받게 된다. 사용 가능한 가장 큰 숫자 형식 타입은 UInt64 로써, 이 타입을 사용할 경우 64 개의 각기 다른 (조합되지 않은) enum 플래그 상수를 선언할 수 있다. enum 키워드는 기본적으로 int 타입을 사용하게 되어 있으며, 이는 실제적으로 Int32 를 나타낸다. 컴파일러는 32 비트보다 더 큰 값을 선언할 수 있도록 허용하지만, 이러한 경우 경고 없이 wrap around 될 것이므로, enum 이 32 개 이상의 플래그 값을 표현해야 하는 경우에는, 기본보다 더 큰 타입을 명시적으로 기술해 주어야 한다:

public enum BigEnum : ulong { BigValue = 1 << 63 }

플래그들은 대부분 하나의 비트만으로 표현되지만, 보다 편리한 사용을 위하여 이름이 주어진 세트로 조합될 수 있다.

[Flags] enum FlagsEnum { None = 0, Option1 = 1, Option2 = 2, Option3 = 4, Default = Option1 | Option3, All = Option1 | Option2 | Option3, }

2 의 제곱수들을 10진수 형태로 일일이 기술하는 불편을 피하기 위하여, 동일한 enum 을 좌측 시프트 연산자 (<<) 를 이용하여 선언할 수도 있다.

[Flags] enum FlagsEnum { None = 0, Option1 = 1 << 0, Option2 = 1 << 1, Option3 = 1 << 2, Default = Option1 | Option3, All = Option1 | Option2 | Option3, }

C# 7.0 부터는, 2진수 리터럴 (literal) 역시 사용 가능하다.

enum 변수의 값이 특정 플래그 세트를 포함하고 있는지를 확인하기 위해서는 HasFlag 메소드를 사용할 수 있다. 예를 들어 다음과 같은 enum 이 있고

[Flags] enum MyEnum { One = 1, Two = 2, Three = 4 }

변수의 값은 다음과 같이 선언되어 있을 때,

var value = MyEnum.One | MyEnum.Two;

HasFlag 를 이용하면 특정 플래그가 설정된 상태인지를 확인할 수 있다.

if(value.HasFlag(MyEnum.One)) Console.WriteLine("Enum has One"); if(value.HasFlag(MyEnum.Two)) Console.WriteLine("Enum has Two"); if(value.HasFlag(MyEnum.Three)) Console.WriteLine("Enum has Three");

또한 enum 의 모든 플래그들을 요소 반복하여 설정된 플래그들의 목록을 확인할 수도 있다.

var type = typeof(MyEnum); var names = Enum.GetNames(type); foreach (var name in names) { var item = (MyEnum)Enum.Parse(type, name); if (value.HasFlag(item)) Console.WriteLine("Enum has " + name); }

또는

foreach(MyEnum flagToCheck in Enum.GetValues(typeof (MyEnum))) { if (value.HasFlag(flagToCheck)) { Console.WriteLine("Enum has " + flagToCheck); } }

위 세가지 예제 모두는 다음과 같은 결과를 출력하게 될 것이다:

Enum has One Enum has Two
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 22.1: Enum 에 대한 기본 사항들

MSDN 의 설명을 참조하면:

열거형 타입 (enumeration 혹은 enum 이라고도 이름붙여진) 은 변수에 할당 가능한 명명된 정수 숫자 형식의 상수 집합 을 선언할 수 있는 효율적인 방법을 제공한다.

기본적으로, enum 은 한정된 집합 내의 선택지만을 허용하는 타입이며 각 선택지는 특정 숫자에 해당하는 값을 가진다. 기본적으로, 이 숫자들은 선언된 순서대로 0 부터 하나씩 증가하는 값들이다. 사용 예를 보면, 요일을 나타내는 enum 을 다음과 같이 선언할 수 있다:

public enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }

이렇게 선언된 enum 은 아래와 같이 사용될 수 있다:

// 변수들로 하여금 특정 요일에 해당하는 값을 갖도록 정의한다 Day myFavoriteDay = Day.Friday; Day myLeastFavoriteDay = Day.Monday; // myFavoriteDay 에 해당하는 int 값을 얻어온다 // 금요일은 숫자 4 에 해당한다 int myFavoriteDayIndex = (int)myFavoriteDay; // 숫자 5 로 표현되는 요일을 얻어온다 Day dayFive = (Day)5;

기본적으로 각 enum 요소들의 내부 타입은 int 이나, byte, sbyte, short, ushort, uint, long 그리고 ulong 타입 역시 사용될 수 있다. int 이외의 타입을 사용하고자 한다면, enum 의 이름 다음에 콜론 (:) 을 사용하여 원하는 타입을 기술하도록 한다:

public enum Day : byte { // 이전과 동일한 부분 }

각 요일의 이름에 해당하는 숫자 값들은 이제 integer 가 아닌 byte 타입이 될 것이다. 특정 enum 이 내부적으로 표현되는 타입 정보는 아래와 같은 방법으로 얻어올 수 있다:

Enum.GetUnderlyingType(typeof(Days)));

출력 결과:

System.Byte

데모: .NET fiddle

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Chapter 22: Enum

enumbyte, sbyte, short, ushort, int, uint, long, ulong 타입 중 어떤 것으로부터도 파생 (derive) 될 수 있다. 기본 타입은 int 이며, enum 을 정의할 때 원하는 타입을 기술함으로써 이를변경할 수 있다:

public enum Weekday : byte { Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5 }

이는 네이티브 코드를 P/Invoke (플랫폼 호출) 한다거나, 데이터 소스와의 매핑을 시키는 등의 상황에서 유용하게 사용할 수 있다. 대부분의 개발자들은 enumint 일 것으로 예상할 것이기 때문에, 일반적인 상황에서는 기본 타입인 int 를 사용하여야 할 것이다.

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 21.1: 배열의 내용을 주어진 값만큼 shift rotate 시키는 generic 메소드 예제

유의할 점 : 주어진 값이 음수일 경우에는 왼쪽으로, 양수일 경우에는 오른쪽으로 shift rotate를 수행한다.

public static void Main() { int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int shiftCount = 1; Rotate(ref array, shiftCount); Console.WriteLine(string.Join(", ", array)); // 출력 결과: [10, 1, 2, 3, 4, 5, 6, 7, 8, 9] int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; shiftCount = 15; Rotate(ref array, shiftCount); Console.WriteLine(string.Join(", ", array)); // 출력 결과: [6, 7, 8, 9, 10, 1, 2, 3, 4, 5] int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; shiftCount = -1; Rotate(ref array, shiftCount); Console.WriteLine(string.Join(", ", array)); // 출력 결과: [2, 3, 4, 5, 6, 7, 8, 9, 10, 1] int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; shiftCount = -35; Rotate(ref array, shiftCount); Console.WriteLine(string.Join(", ", array)); // 출력 결과: [6, 7, 8, 9, 10, 1, 2, 3, 4, 5] } private static void Rotate < T > (ref T[] array, int shiftCount) { T[] backupArray = new T[array.Length]; for (int index = 0; index < array.Length; index++) { backupArray[(index + array.Length + shiftCount % array.Length) % array.Length] = array[index]; } array = backupArray; }

이 코드에서 중요한 부분은 shift rotate 를 시킨 이후의 인덱스를 계산해 내는 코드이다.

(index + array.Length + shiftCount % array.Length) % array.Length

아래에 조금 더 상세한 내용이 기술되어 있다:

(shiftCount % array.Length) -> shift 를 시킬 값의 크기가 배열의 크기 한도 내에서만 존재하도록 값을 정규화 (normalize) 시킨다 (예를 들어 크기가 10 인 배열이라면, shift 연산을 1 만큼 수행하는 것과 11 만큼 수행하는 것은 동일한 결과를 나타낼 것이다. 이는 -1-11 에 대해서도 동일하게 적용된다).

array.Length + (shiftCount % array.Length) -> 이는 좌측 방향으로의 rotate 수행 시 인덱스값이 음수가 되는 것을 방지하고, 대신에 배열의 끝쪽으로 rotate 가 수행되도록 한다. 이러한 코드가 없다면 크기가 10 인 배열에 대해서 index0 이고 shift 시킬 값이 -1 이라면 계산된 인덱스값은 음수 (-1) 가 될 것이며 실제로 필요한 인덱스 값인 9 를 계산해내지 못할 것이다. (10 + (-1 % 10) = 9)

index + array.Length + (shiftCount % array.Length) -> 위에서 설명한 방식을 이용하여 각 index 에 대해 새로이 rotate 된 인덱스 값을 계산해 낸다. (0 + 10 + (-1 % 10) = 9)

index + array.Length + (shiftCount % array.Length) % array.Length -> 두번째 정규화 (normalization) 를 통해 새로운 인덱스 값이 배열이 범위를 벗어나지 않고 배열의 앞쪽으로 rotate될 수 있도록 한다. 이는 우측으로 rotate 되는 경우를 위함으로, 이 코드가 없다면 배열의 크기가 10 이고 shift 시킬 값이 1 인 상황에서 index9 가 될 경우 새로운 인덱스 값은 10 이 될 것이며 실제로 필요한 인덱스 값인 0 을 계산해내지 못할 것이다. ((9 + 10 + (1 % 10)) % 10 = 0)

본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 20.12: 하나의 배열이 다른 배열의 내용을 포함하고 있는지 검사하기

public static class ArrayHelpers { public static bool Contains < T > (this T[] array, T[] candidate) { if (IsEmptyLocate(array, candidate)) return false; if (candidate.Length > array.Length) return false; for (int a = 0; a <= array.Length - candidate.Length; a++) { if (array[a].Equals(candidate[0])) { int i = 0; for (; i < candidate.Length; i++) { if (false == array[a + i].Equals(candidate[i])) break; } if (i == candidate.Length) return true; } } return false; } static bool IsEmptyLocate < T > (T[] array, T[] candidate) { return array == null || candidate == null || array.Length == 0 || candidate.Length == 0 || candidate.Length > array.Length; } }

예제:

byte[] EndOfStream = Encoding.ASCII.GetBytes("---3141592---"); byte[] FakeReceivedFromStream = Encoding.ASCII.GetBytes("Hello, world!!!---3141592---"); if (FakeReceivedFromStream.Contains(EndOfStream)) { Console.WriteLine("Message received"); }
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

Section 20.11: IEnumerable<> 인스턴스로의 배열

모든 배열들은 non-generic 버전의 IList 인터페이스를 (더불어 상위 인터페이스인 non-generic 버전의 ICollectionIEnumerable 를 포함하여) 구현 (implement) 하게 되어 있다.

더욱 중요한 점은, 1차원 배열들의 경우 해당 배열의 데이터 타입에 대하여 IList<>IReadOnlyList<> generic 인터페이스를 (더불어 이들의 상위 인터페이스를 포함하여) 구현 (implement) 하게 되어 있다는 점이다. 이것이 의미하는 바는 이러한 배열들이 generic 한 열거형 (enumerable) 타입으로 취급될 수 있다는 것으로, 배열이 아닌 다른 형태로 변환시킬 필요 없이 다양한 함수들의 파라미터로 바로 사용될 수 있게 된다.

int[] arr1 = { 3, 5, 7 }; IEnumerable<int> enumerableIntegers = arr1; //배열이 IEnumerable<T> 를 구현 (implement) 하므로 변환이 허용된다 List<int> listOfIntegers = new List<int>(); listOfIntegers.AddRange(arr1); // List 의 내용물을 채우기 위해 배열에 대한 reference 를 사용할 수 있다.

위 코드 실행 시, listOfIntegers3, 5, 그리고 7 의 값을 포함하는 List<int> 를 갖게 될 것이다. IEnumerable<> 로의 변환 지원을 통해, 배열 내용을 LINQ 를 사용하여 조회할 수도 있다.

다음 예제를 확인한다.

arr1.Select(i => 10 * i)
본 문서는 C# Notes for Professionals (라이센스:CC-BY-SA) 를 한글로 번역한 문서입니다. 번역상 오류가 있을 수 있으므로 정확한 내용은 원본 문서를 참고하세요.

[출처] https://books.goalkicker.com/CSharpBook/

반응형

+ Recent posts