성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# 7.3에서 enum을 boxing 없이 int로 변환하기 - 세 번째 이야기</h1> <p> 예전에 쓴 글에 이어서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C#에서 enum을 boxing 없이 int로 변환하기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11270'>http://www.sysnet.pe.kr/2/0/11270</a> C#에서 enum을 boxing 없이 int로 변환하기 - 두 번째 이야기 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11506'>http://www.sysnet.pe.kr/2/0/11506</a> </pre> <br /> 이번에는 C# 7.3부터 새롭게 추가된 문법을 이용하여 저 코드를 개선해 보겠습니다.<br /> <br /> 우선 <a target='tab' href='http://www.sysnet.pe.kr/2/0/11270'>첫 번째 글</a>을 정리해 보면, enum 타입을 명시적인 형변환 외에는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > enum States { Wait, Run, } int n1 = (int)States.Run; int n2 = (int)States.Wait; </pre> <br /> 박싱 없이 사용할 수 없다는 것입니다. 그래서 <a target='tab' href='http://www.sysnet.pe.kr/2/0/11506'>두 번째 글</a>에서와 같이 System.TypedReference와 __makeref 예약어를 사용해 해결해 보았지만, 아쉽게도 __makeref는 다중 런타임에서의 호환성이 부족한 문제가 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > (Unity가 사용하는) 모노 런타임의 __makeref 오류 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11564'>http://www.sysnet.pe.kr/2/0/11564</a> </pre> <br /> 그런데, 마침 C# 7.3에서 적절한 문법이 하나 나왔습니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# 7.3 - unmanaged(blittable) 제네릭 제약 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11558'>http://www.sysnet.pe.kr/2/0/11558</a> </pre> <br /> 이것을 이용하면 순수 C#의 공식 문법만 사용해 저 문제를 해결할 수 있습니다. 바로 이렇게!<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class WrapperObject<<span style='color: blue; font-weight: bold'>TEnum</span>, TValue> where <span style='color: blue; font-weight: bold'>TEnum : unmanaged</span> { TValue[] data; public WrapperObject(int count) { data = new TValue[count]; } public TValue this[TEnum key] { get { return data[ConvertToIndex(key)]; } set { data[ConvertToIndex(key)] = value; } } public unsafe int ConvertToIndex(TEnum key) { <span style='color: blue; font-weight: bold'>TEnum *idx = &key; int* intPtr = (int*)idx; return *intPtr;</span> } /* unsafe int ConvertToIndex(TEnum key) { System.TypedReference reference = __makeref(key); System.TypedReference* pRef = &reference; int* valuePtr = (int*)*((IntPtr*)&reference); return *valuePtr; } */ } </pre> <br /> TEnum 타입에 unmanaged 제약을 걸었으니, 이를 포인터로 자연스럽게 받아올 수 있고 그 포인터로부터 값을 참조해 반환하면 박싱 연산이 전혀 발생하지 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > WrapperObject<States, IState> states = new WrapperObject<States, IState>(2); Console.WriteLine("Run == " + states.ConvertToIndex(States.Run)); // 출력: 0 Console.WriteLine("Wait == " + states.ConvertToIndex(States.Wait)); // 출력: 1 </pre> <br /> <hr style='width: 50%' /><br /> <br /> __makeref를 사용할 때와 달리 unmanaged 제약을 사용하면 enum 타입이 byte, short, long 기반이어도 그에 대응해 포인터 연산을 할 수 있어 안전한 참조를 할 수 있습니다. 왜냐하면, unmanaged 제약의 경우 sizeof 연산자를 이용한 크기 반환도 가능하기 때문입니다. 예를 들어, C# 7.2 이전에는 다음과 같이 오류가 발생하는 코드가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class WrapperObject<<span style='color: blue; font-weight: bold'>TEnum</span>, TValue> <span style='color: blue; font-weight: bold'>where TEnum : struct</span> { // ...[생략]... unsafe public WrapperObject(int count) { data = new TValue[count]; // unmanaged 이외의 제약으로는 sizeof 연산자를 사용할 수 없음. // 컴파일 오류 - Error CS0208 Cannot take the address of, get the size of, or declare a pointer to a managed type ('TEnum') <span style='color: blue; font-weight: bold'>_enumSize = sizeof(TEnum);</span> } } </pre> <br /> C# 7.3의 unmanaged 제약으로는 컴파일할 수 있게 되므로, 이를 근간으로 다음과 같이 enum의 기반 타입에 맞게 포인터 대응을 할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class WrapperObject<<span style='color: blue; font-weight: bold'>TEnum</span>, TValue> <span style='color: blue; font-weight: bold'>where TEnum : unmanaged</span> { TValue[] data; int _enumSize = 0; unsafe public WrapperObject(int count) { data = new TValue[count]; // C# 7.3 이후 제공되는 unmanaged 제약으로는 sizeof 연산자를 사용 가능 <span style='color: blue; font-weight: bold'>_enumSize = sizeof(TEnum);</span> } public TValue this[TEnum key] { get { return data[ConvertToIndex(key)]; } set { data[ConvertToIndex(key)] = value; } } public unsafe int ConvertToIndex(TEnum key) { TEnum* idx = &key; <span style='color: blue; font-weight: bold'>// enum 타입의 크기에 맞게 포인터 연산 switch (_enumSize) { case 1: byte* bytePtr = (byte*)idx; return *bytePtr; case 2: short* shortPtr = (short*)idx; return *shortPtr; case 4: int* intPtr = (int*)idx; return *intPtr; case 8: long* longPtr = (long*)idx; return (int)*longPtr; }</span> return -1; } } </pre> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1278&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> C# 7.3의 이러한 매끄러운 해결 방법은, 그래도 현재 .NET Core, .NET Framework 이외의 런타임에서는 사용할 수 없습니다. Mono의 경우 아직 C# 7.0조차도 100% 지원하지 못하고 있으므로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# Compiler ; <a target='tab' href='http://www.mono-project.com/docs/about-mono/languages/csharp/'>http://www.mono-project.com/docs/about-mono/languages/csharp/</a> </pre> <br /> 모노 런타임을 근간으로 한 Unity 역시 아직 위와 같은 코드를 사용할 수는 없습니다.<br /> <br /> 그래도 ^^ __makeref가 지원되는 것을 Mono에 기대하는 것보다는, C# 7.3의 문법이 제공되는 쪽이 더 희망적일 것이므로 시간이 좀 필요한 정도일 것입니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1071
(왼쪽의 숫자를 입력해야 합니다.)