성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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# - blittable 타입이란?</h1> <p> 닷넷에서 blittable 타입에 대한 공식 문서는 다음에서 찾아볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Blittable and Non-Blittable Types ; <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types'>https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types</a> </pre> <br /> 위의 문서에 따르면 기존 타입들 중 다음의 것들이 blittable 타입입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > System.Byte (byte) System.SByte (sbyte) System.Int16 (short) System.UInt16 (ushort) System.Int32 (int) System.UInt32 (uint) System.Int64 (long) System.UInt64 (ulong) System.IntPtr System.UIntPtr System.Single (float) System.Double (double) </pre> <br /> 이와 함께 다음의 것들도 blittable 타입입니다.<br /> <br /> <ul> <li>One-dimensional arrays of blittable types, such as an array of integers. However, a type that contains a variable array of blittable types is not itself blittable.</li> <li>Formatted value types that contain only blittable types (and classes if they are marshaled as formatted types). For more information about formatted value types, see Default Marshaling for Value Types.</li> </ul> <br /> 첫 번째 조건을 풀어보면, blittable types의 1차원 배열도 blittable 타입이지만, 반면 배열을 포함한 타입은 blittable 타입이 아니라는 것입니다. 예를 들어 다음의 arr은 blittable 타입의 인스턴스이지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > void Main() { <span style='color: blue; font-weight: bold'>int [] arr</span> = new int [100]; } </pre> <br /> 다음의 TestStruct 타입은 배열을 포함하고 있으므로 blittable 타입이 아닙니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > struct TestStruct { public int Age; // int만 담고 있다면 TestStruct도 blittable 타입이겠지만. public <span style='color: blue; font-weight: bold'>int []</span> Scores; // 배열을 담고 있으므로 TestStruct는 blittable 타입이 아님. } </pre> <br /> 두 번째 조건은 복합 타입(complex type)인 경우에 대해 blittable 조건을 명시하고 있습니다. 멤버로 blittable 타입, 또는 formatted 타입으로 마샬링 가능한 클래스만을 필드로 포함하는 "Formatted value types"라면 그 타입은 blittable이라는 것입니다. "Formatted" + "value types"이기 때문에 기본적으로 class 타입은 무조건 blittable이 아닙니다. 그리고 나머지 "Formatted 타입"이라는 것은 다음의 문서에서 설명하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Default Marshaling for Value Types ; <a target='tab' href='https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/0t2cwe11(v=vs.100)'>https://docs.microsoft.com/en-us/previous-versions/dotnet/netframework-4.0/0t2cwe11(v=vs.100)</a> </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > A formatted type is a complex type that contains information that explicitly controls the layout of its members in memory. </pre> <br /> 결국 타입이 담고 있는 필드들에 대한 메모리 상의 정보를 가지고 있다면 "A formatted type"입니다. 그리고 그 정보라는 것은 StructLayout 특성과 연결됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 눈으로 확인하는 LayoutKind 옵션 효과 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1558'>http://www.sysnet.pe.kr/2/0/1558</a> </pre> <br /> 정리해 보면, "Formatted value types"는 값 형식 중에서 LayoutKind.Auto가 아닌 형식을 의미합니다. 모든 타입은 LayoutKind가 명시되어 있지 않으면 Auto가 되지만, 특별히 C# 컴파일러는 struct 타입에 한해 (사용자가 지정하지 않았다면) 자동으로 LayoutKind.Sequential을 지정해 줍니다. 따라서 사용자가 굳이 Auto로 지정하지 않은 struct 타입에 대해 오직 blittable 타입만을 필드로 포함하고 있다면 그 formatted value type도 blittable 타입이 됩니다.<br /> <br /> 따라서 struct인 경우 다음은 blittable이지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > struct BlittableStructType { public int Age; } </pre> <br /> 다음의 struct들은 blittable이 아닙니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [StructLayout(LayoutKind.Auto)] // Auto Layout이므로. struct AutoLayoutStructType { public int Age; } struct NonBlittableStructType_HasString { public int Age; public string Name; // 참조 타입인 string을 포함 } struct NonBlittableStructType_HasArray { public int Age; public int [] data; // 참조 타입인 배열을 포함 } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 일단 문서는 그렇다 치고, 개인적으로 한 가지 혼란스러운 문구가 하나 있습니다.<br /> <br /> <pre style='white-space: pre-wrap; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Formatted value types that contain only blittable types <span style='color: blue; font-weight: bold'>(and classes if they are marshaled as formatted types)</span>. For more information about formatted value types, see Default Marshaling for Value Types. </pre> <br /> 위에서 말하는 "classes"에 속한 타입은 다음과 같은 것들을 의미합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [StructLayout(LayoutKind.Explicit)] class ExplicitClassType { [FieldOffset(0)] public int Age; } </pre> <br /> 그리고 문서에 의하면 저런 "classes"들을 포함하는 "Formatted value types"가 blittable하다고 합니다. 예를 들어 다음과 같은 struct가 blittable일 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > struct StructType { public int Age; public ExplicitClassType data1; } </pre> <br /> blittable이 되기 위한 기본 조건은 문서의 처음에 언급한 대로,<br /> <br /> <pre style='white-space: pre-wrap; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Most data types have a <span style='color: blue; font-weight: bold'>common representation in both managed and unmanaged memory</span> and <span style='color: blue; font-weight: bold'>do not require special handling by the interop marshaler</span>. <span style='color: blue; font-weight: bold'>These types are called blittable types</span> because they do not require conversion when they are passed between managed and unmanaged code. </pre> <br /> 해당 타입의 필드들이 managed/unmanaged 메모리에서 동일한 구성을 갖고 있어 "interop marshaler"에 의한 별도 처리가 필요하지 않은 것입니다. 좀 더 간단하게 표현하면, 그 인스턴스가 존재하는 메모리를 포인터로 가리킬 때 그 주소 영역에 모든 필드의 값들이 포함되어 있어야 합니다. 하지만 위의 StructType은 ExplicitClassType이 "marshaled as formatted types"에 속하고 따라서 그것의 메모리 크기까지 모두 계산이 되어 할당은 되지만 그래도 managed <-> unmanaged 간의 마샬링 절차가 필요합니다. 즉, 아래에서 설명할 interop marshaler를 고려해 보면 ExplicitClassType을 포함한 StructType 타입이 blittable 타입이라고 볼 수 없는 것입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 비록, 인스턴스의 필드들이 메모리의 연속된 공간에 펼쳐져 있지만 "do not require special handling by the interop marshaler" 조건을 만족시키지 못해 blittable이 아닌 타입도 있습니다. "<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types'>Blittable and Non-Blittable Types</a>" 문서에도 나오지만,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > System.Array System.Boolean System.Char System.Class System.Object System.Mdarray System.String System.Valuetype System.Szarray </pre> <br /> 위의 것들은 Non-Blittable입니다. 이상한 것은 이 중에서 3개(System.Class, System.Mdarray, System.Szarray)는 원래 .NET BCL에 존재하지 않는 타입이고, 또 다른 3개(System.Array, System.Object, System.ValueType)는 딱히 자료형으로 쓰지 않는 타입입니다. 따라서 남은 자료형은 3개인데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > System.Boolean System.Char System.String </pre> <br /> 이게 왜 Non-Blittable이 아닌지 살짝 이해가 안 될 수도 있습니다. 이유는, 문서 상으로 보면 위의 것들은 interop marshaler가 그 중간에서 작업을 해줘야 하기 때문입니다. <br /> <br /> 우선 System.Boolean의 경우, 1바이트인데 unmanaged 공간에서는 BOOL 타입이 C/C++라면 1 바이트이거나 COM VARIANT에서는 short이기 때문에 중간에 interop marshaler가 변환 처리를 해야 합니다. (심지어 4바이트가 될 때도 있다고 합니다.)<br /> <br /> System.Char는 managed 공간에서는 2바이트지만 unmanaged 공간으로 갈 때는 ANSI/UNICODE 2가지 버전으로 나뉠 수 있기 때문에 역시 중간에서 interop marshaler가 관여를 합니다.<br /> <br /> 마지막으로 System.String은 결국 System.Char의 연속 공간이기 때문에 마찬가지로 interop marshaler가 관여를 합니다. 재미있는 것은 System.String의 경우 설령 interop marshaler가 관여를 하지 않는다 해도 blittable 타입이 될 수 없습니다. 확인을 위해 .NET Reflector로 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [Serializable, ComVisible(true), __DynamicallyInvokable] public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable, IComparable<string>, IEnumerable<char>, IEquatable<string> { // Fields // ...[생략]... [NonSerialized] private char m_firstChar; [NonSerialized] private int m_stringLength; // ...[생략]... } </pre> <br /> System.String 타입이 포함하고 있는 필드는 char m_firstChar, int m_stringLength일 뿐 문자 배열 자체는 CLR 내부에 의해 숨겨져 있기 때문입니다.<br /> <br /> 그 이외의 타입들에 대해서는 interop marshaler는 대상 타입이 참조 객체를 포함하지 않고 LayoutKind.Auto만 아니라면 관여하지 않을 수 있습니다. 하지만, 그 이외의 경우라면 관여를 하게 됩니다.<br /> <br /> 우선 대상 타입이 Sequential이고, 그것이 ("classes if they are marshaled as formatted types" 조건에 해당하는) 참조 객체를 포함하고 있는 경우 managed와 unmanaged 간의 메모리 크기도 상이할뿐더러 필드 배치의 순서까지도 달라질 수 있으므로 당연히 interop marshaler가 중재할 수밖에 없습니다.<br /> <br /> 또한 대상 타입이 Explicit 일지라도 managed와 unmanaged 간의 메모리 구조가 동일하다는 것만 빼고는 여전히 managed일 때 참조 타입인 필드에 대해서는 힙 주소만을 포함하고 있다가 unmanaged로 마샬링이 되어야 할 때 interop marshaler의 중재로 메모리가 동일하게 바뀌게 됩니다. 이 때문에 "Formatted value types"가 blittable이 되려면 어찌 되었건 필드로 참조 타입을 가져서는 안됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그건 그렇고, 혹시 해당 타입이 blittable 타입인지 코드로 알 수 있을까요? 아쉽게도 닷넷 차원에서는 이것을 알 수 있는 직접적인 정보를 Type 클래스에서 제공하지 않습니다. 예전에도 ^^ 값 형식인지 참조 형식인지에 대한 구분도 메타데이터에서 제공하지 않는다고 했었는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 닷넷 메타데이터에 struct/class(값/참조 형식)의 구분이 있을까요? ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/10993'>http://www.sysnet.pe.kr/2/0/10993</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;' > The fastest way to check if a type is blittable? ; <a target='tab' href='https://stackoverflow.com/questions/10574645/the-fastest-way-to-check-if-a-type-is-blittable'>https://stackoverflow.com/questions/10574645/the-fastest-way-to-check-if-a-type-is-blittable</a> BlittableStructs/BlittableStructs/BlittableHelper.cs ; <a target='tab' href='https://github.com/AndreyAkinshin/BlittableStructs/blob/master/BlittableStructs/BlittableHelper.cs'>https://github.com/AndreyAkinshin/BlittableStructs/blob/master/BlittableStructs/BlittableHelper.cs</a> </pre> <br /> 그런데, 제가 정리한 이 글에서의 요구를 만족시키진 않습니다. 왜냐하면 참조 형식(class)도 blittable이라고 반환하는 경우가 있기 때문입니다. 따라서 이 글에서 정리한 조건을 만족하려면 다음과 같이 if 문 하나를 더 추가해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public static class IsBlittableCache<T> { public static bool IsBlittable() { return IsBlittableCache<T>.Value; } public static bool IsBlittable(Type type) { if (type.IsArray) { var elem = type.GetElementType(); return elem.IsValueType && IsBlittable(elem); } <span style='color: blue; font-weight: bold'>if (type.IsValueType == false) { return false; }</span> try { object instance = FormatterServices.GetUninitializedObject(type); GCHandle.Alloc(instance, GCHandleType.Pinned).Free(); return true; } catch { return false; } } public static readonly bool Value = IsBlittable(typeof(T)); } </pre> <br /> 위의 코드로 몇몇 타입을 테스트해 보면 다음과 같은 결과를 얻을 수 있습니다. (아래의 출력에 사용된 타입의 구체적인 정의는 <a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1274&boardid=331301885'>첨부 파일</a>에서 확인할 수 있습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ClassType: False SequentialClassType: False SequentialClassType_HasString: False ExplicitClassType: False ExplicitClassType_HasString: False AutoLayoutStructType: False BlittableStructType: True NonBlittableStructType_HasClassWithLayoutInfo: False NonBlittableStructType_HasClassWithLayoutInfo2: False NonBlittableStructType_HasClassWithLayoutInfo3: False NonBlittableStructType_HasString: False NonBlittableStructType_HasArray: False System.Boolean: False System.Char: False System.Int32[]: True System.Boolean[]: False BlittableStructType[]: True System.String: False </pre> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1274&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2035
(왼쪽의 숫자를 입력해야 합니다.)