성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Reordering on an Alpha processor ;...
[정성태] 공유 감사합니다. ^^ 참고로, WPF에서 WindowsF...
[Tom Lee] 답변 감사합니다. 나름의 해결책 연구해보고 여기에도 공유해봅니다...
[정성태] 아래의 글을 보면, MoveWindow 하면 될 듯한데요. ^^...
[Tom Lee] 안녕하세요 올려주신 글 참고하여 WPF 어플리케이션 안에 Uni...
[정성태] A graphical depiction of the steps ...
[정성태] 질문을 주셔서 출판사 측에 문의를 했습니다. 약 한 달 정도 후...
[Thorondor
] @정성태 개인 블로그인데도 거의 커뮤니티 급 인 것 같아요. 요...
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
글쓰기
제목
이름
암호
전자우편
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'>닷넷의 관리 포인터(Managed Pointer)와 System.TypedReference</h1> <p> 관리 포인터(Managed Pointer)가 그동안 C# 문법에서 "ref" 예약어를 이용해 사용할 수 있었지만, 사실 "참조"라는 말로 대신 이해해도 되었기 때문에 굳이 그것의 특성에 관해 몰라도 상관없었습니다. 그런데, C# 7.2부터 추가된 기능들을 (사용이 아닌) 이해하기 위해서는 관리 포인터에 대한 사전 지식을 갖추는 것이 좋습니다.<br /> <br /> 이에 대해 다음의 2가지 글이 아주 잘 설명해 주고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# Futures: Managed Pointers ; <a target='tab' href='https://www.infoq.com/news/2015/04/CSharp-7-Pointers'>https://www.infoq.com/news/2015/04/CSharp-7-Pointers</a> Managed pointers ; <a target='tab' href='http://mustoverride.com/managed-refs-CLR/'>http://mustoverride.com/managed-refs-CLR/</a> </pre> <br /> C# 6.0 이전까지 관리 포인터는 매개 변수의 ref 구문으로 사용할 수 있었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; class Program { static void Main(string[] args) { Program pg = new Program(); StructPerson sarah = new StructPerson() { Name = "Kerrigan", Age = 27 }; pg.PassByManagedPointer(<span style='color: blue; font-weight: bold'>ref</span> sarah); } private void PassByManagedPointer(<span style='color: blue; font-weight: bold'>ref</span> StructPerson sarah) { } } struct StructPerson { public int Age; public string Name; } </pre> <br /> 그러다가 C# 7.0에서 로컬 변수와 메서드의 반환값에 대해 관리 포인터를 사용하는 것이 가능해졌습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class Program { static void Main(string[] args) { Program pg = new Program(); { int a = 5; <span style='color: blue; font-weight: bold'>ref int refA = ref a;</span> <span style='color: blue; font-weight: bold'>ref int refB = ref pg.GetRef(ref a);</span> refB = 10; Console.WriteLine(a); // 출력 결과 10 } } private ref int GetRef(ref int a) { return ref a; } } </pre> <br /> 이후 C# 7.2부터는 3항 연산자에도 ref를 사용할 수 있게 했습니다.<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.2 - 3항 연산자에 ref 지원 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11528'>http://www.sysnet.pe.kr/2/0/11528</a> </pre> <br /> 그런데, 여기서 한 가지 재미있는 점이 있습니다. 로컬 변수, 매개 변수, 반환 값에도 가능한 관리 포인터가 클래스/구조체의 필드로는 정의할 수 없다는 것입니다. (업데이트: C# 11부터 ref struct에 한해 필드로 정의할 수 있습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > struct StructPerson { public int Age; public string Name; // 컴파일 오류: CS0501 'StructPerson.Height()' must declare a body because it is not marked abstract, extern, or partial <span style='color: blue; font-weight: bold'>ref int Height;</span> } </pre> <br /> 그 이유는 "<a target='tab' href='http://mustoverride.com/managed-refs-CLR/'>Managed pointers</a>" 글에서 설명하고 있습니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Fields and array elements are not permitted to have & types. & cannot be boxed either. These restrictions are a bit artificial. <span style='color: blue; font-weight: bold'>It just makes the job of GC easier if & themselves are never on the heap.</span> </div><br /> <br /> 그러니까, 관리 포인터를 필드로 갖는 타입을 정의하게 되면 GC 구현이 더 난해해지기 때문에 의도적으로 제약을 둔 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 관리 포인터를 다뤘으니 이제 System.TypedReference를 설명할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > TypedReference Structure ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.typedreference'>https://learn.microsoft.com/en-us/dotnet/api/system.typedreference</a> </pre> <br /> TypedReference 타입은 .NET 1.1부터 있어왔지만 사실 개발자들이 거의 사용하지 않는 타입입니다. 저도 이에 관해 이야기한 적이 딱 2번 있었군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - 구조체(값 형식)의 필드를 리플렉션을 이용해 값을 바꾸는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11312'>http://www.sysnet.pe.kr/2/0/11312</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 /> 문서에 있는 바대로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Describes objects that contain both a <span style='color: blue; font-weight: bold'>managed pointer</span> to a location and a <span style='color: blue; font-weight: bold'>runtime representation of the type</span> that may be stored at that location. </pre> <br /> TypedReference는 내부적으로 관리 포인터와 대상 타입의 정보를 보관하고 있습니다. "관리 포인터"를 가지고 있다는 것은 곧, 위에서 이야기했던 ref의 제약을 그대로 가지고 있음을 의미합니다. 즉, "관리 포인터"가 힙 안에 보관될 수 없기 때문에 "관리 포인터"를 소유한 TypedReference 역시 힙 안에 보관될 수 없으므로 타입의 필드로 정의될 수 없습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > struct StructPerson { public int Age; public string Name; // 컴파일 오류: CS0610 Field or property cannot be of type 'TypedReference' public TypedReference tr; } </pre> <br /> 대신 ref와 유사하게 (반환을 제외한) 로컬 변수와 매개 변수로 사용하는 것은 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; class Program { static int _number; static void Main(string[] args) { int x = 3; <span style='color: blue; font-weight: bold'>System.TypedReference xRef</span> = __makeref(x); { <span style='color: blue; font-weight: bold'>PassTR(xRef);</span> } { System.TypedReference tr = GetTR(); } } // 컴파일 오류: CS1599 Method or delegate cannot return type 'TypedReference' private static <span style='color: blue; font-weight: bold'>TypedReference GetTR</span>() { TypedReference tr = __makeref(_number); return tr; } private static void PassTR(<span style='color: blue; font-weight: bold'>TypedReference tr</span>) { } } </pre> <br /> 그런데 왜? ref는 메서드의 반환으로 사용할 수 있으면서 TypedReference는 안되는 걸까요? 그 이유는, ref의 경우에도 안전하지 않은 반환 유형이 있는데 그것을 TypedReference에 적용할 수 없기 때문입니다. (아마도 ref가 그렇듯이 추적할 수는 있겠지만 굳이 그런 체크를 추가하진 않은 듯합니다.) 예를 들어, 로컬 변수에 대한 참조는 ref 예약어로도 반환할 수 없습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static ref int GetRefInt1() { int number = 10; <span style='color: blue; font-weight: bold'>return ref number; // CS8168 Cannot return local 'number' by reference because it is not a ref local</span> } private static ref int GetRefInt2() { int number = 10; ref int refA = ref number; ref int refB = ref refA; // 결국 로컬 변수의 참조이므로! <span style='color: blue; font-weight: bold'>return ref refB; // 컴파일 오류: CS8157 Cannot return 'refB' by reference because it was initialized to a value that cannot be returned by reference</span> } </pre> <br /> 위와 같이 C# 컴파일러는 ref가 적용된 참조 변수에 대해서는 그 대상이 로컬 변수인지를 관리할 수 있는 반면, TypedReference에 대해서는 관리를 못해 아예 반환값으로는 사용할 수 없도록 제약을 둔 것입니다.<br /> <br /> 이와 함께 TypedReference의 경우 자신이 정의하지 않은 메서드에 대한 호출은 모두 (내부적으로 관리 힙에 놓일 가능성이 있으므로) 허용되지 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > int number = 50; TypedReference tr = __makeref(number); // 컴파일 오류: CS0029 Cannot implicitly convert type 'System.TypedReference' to 'object' TypedReference.<span style='color: blue; font-weight: bold'>ReferenceEquals</span>(obj1, obj2); // 컴파일 오류: CS0029 Cannot implicitly convert type 'System.TypedReference' to 'System.ValueType' tr.<span style='color: blue; font-weight: bold'>ToString</span>(); // 컴파일 오류: CS0029 Cannot implicitly convert type 'System.TypedReference' to 'object' tr.<span style='color: blue; font-weight: bold'>GetType</span>(); </pre> <br /> 반면 Equals와 GetHashCode에 대해서는 TypedReference가 override하고 있기 때문에 호출은 가능하지만 그나마도 Equals의 경우 지원하지 않는다는 예외를 발생시키도록 재정의되었고 오직 동작하는 것은 GetHashCode 하나입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 런타임 에러 - An unhandled exception of type 'System.NotSupportedException' occurred in mscorlib.dll // This feature is not currently implemented. tr.<span style='color: blue; font-weight: bold'>Equals</span>(null); int result = tr.<span style='color: blue; font-weight: bold'>GetHashCode</span>(); </pre> <br /> 아울러 (절대로 "힙"위에는 올라가서는 안되므로) 배열로도 생성할 수 없습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // 런타임 오류: System.TypeLoadException: 'Could not create array type 'System.TypedReference[]' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.' Assembly.Load("mscorlib.dll").GetType("System.<span style='color: blue; font-weight: bold'>TypedReference[]</span>"); </pre> <br /> 결국 이러한 TypedReference 타입의 성격을 한마디로 표현하면, "스택에서만 존재할 수 있는 타입"이라는 점입니다. <br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1257&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9755
(왼쪽의 숫자를 입력해야 합니다.)