2016-06-17 3 views
3

는 I는 구조체의 단독 표현에 영향 documentation for [FieldOffset] 따르면 C 번호.NET의 통합 필드 - 실제로 관리되는 코드에서 작동 할 수 있습니까?

[StructLayout(LayoutKind.Explicit)] 
public struct MyUnion 
{ 
    [FieldOffset(0)] 
    public string MyString; 
    [FieldOffset(0)] 
    public Version MyVersion; 
} 

이 같은 구조체를 정의. 그러나 놀랍게도 관리되는 코드에서 제대로 작동하는 것 같습니다. dotTrace에서 메모리 사용량을 프로파일 링 할 때 각 MyUnion 인스턴스는 하나의 포인터 크기 (x64의 경우 8 바이트)입니다! 값은 여전히 ​​완벽하게 안전합니다.

var stringInside = new MyUnion { MyString = "The string" }; 
var versionInside = new MyUnion { MyVersion = new Version(1, 2, 3, 4) }; 
Console.WriteLine(stringInside.MyString); // The string 
Console.WriteLine(versionInside.MyVersion); // 1.2.3.4 

하지만 잘못된 필드에 액세스하면 어떻게됩니까?

var whatIsThis = stringInside.MyVersion; 
var andThis = versionInside.MyString; 
Console.WriteLine("{0} (type = {1})", whatIsThis, whatIsThis.GetType().FullName); // The string (type = System.String) 
Console.WriteLine("{0} (type = {1})", andThis, andThis.GetType().FullName); // 1.2.3.4 (type = System.Version) 

포함 된 객체의 실제 타입이 보존하지만, 물론 지금은 어떤 컴파일러가 생각하는 어떤 런타임이 생각하는, 예를 들어, 사이에 분리가되어 있다는 의미에서이 여전히 "작품"

Console.WriteLine("Compiler: is it a string? {0}", versionInside.MyString is string); // True 
Console.WriteLine("Runtime: is it a version? {0}", versionInside.MyString.GetType() == typeof(Version)); // True 

이렇게 노조를 사용하는 것은 얼마나 위험합니까? 내가 여기있는 행동에 의지 할 수 있을까요? 다른 방법으로 부서지기 쉬운가요? 특히 이런 코드를 사용하는 것이 안전할까요?

if (versionInside.MyString.GetType() == typeof(string)) 
{ 
    Console.WriteLine("OK, it's a string, use the MyString field"); 
} 
else 
{ 
    Console.WriteLine("OK, it's a Version, use the MyVersion field"); 
} 
+0

노동 조합의 목적은 전통적으로 메모리를 절약하는 것입니다. 실제로 이것은 여전히 ​​문제입니까? 오늘날 복잡성/난독 화 가치가 있습니까? –

+1

예, 물론입니다. 데이터가 충분하면 메모리가 항상 문제가됩니다. – EM0

+0

잘 문서화하십시오. - 어쨌든 50 세 이하의 개발자들과 함께 놀랍게도 몇 명의 개발자를 만나게 될 것입니다. –

답변

2

괜찮습니다. 지원되지 않는 유일한 시나리오는 값 유형 필드를 참조 유형 필드와 겹치게하는 것입니다. 이제 GC는 더 이상 값에 객체 참조가 포함되는지 여부를 신뢰할 수 없게 결정할 수 없습니다. CLR이 비상 정지를 일찍 슬램하게하면 TypeLoadException이 발생합니다.

이러한 공용체의보다 일반적인 형태는 discriminated union입니다. variant type이 표준 예입니다. 필드의 유형을 나타내는 다른 필드가 있습니다. 실제로이 예제가 예제에 포함되어 있으며, 모든 오브젝트에는 해당 유형을 나타내는 숨겨진 필드가 있습니다. "유형 핸들"또는 "메소드 테이블 포인터"라고합니다. Object.GetType()은이를 사용합니다. 그리고 가비지 컬렉터가 객체의 실제 유형을 발견하는 데 사용하는 필드 인 선언 된 유형은 기본 클래스 또는 인터페이스 일 수 있으므로 유용하지 않습니다.

두 값 유형 값을 겹칠 때 필연적으로 문제가 발생합니다. 이제는 다른 필드가 없다면 더 이상 실제 유형을 알 수 없습니다. 잘못된 것을 사용하면 쓰레기를 읽습니다. 쓰기는 메모리 손상을 일으킬 수 없으며 구조는 가장 큰 유형을 포함 할만큼 충분히 큽니다. 그런 종류의 문제는 결코 진단하거나 예측하기가 어렵지 않습니다.

+0

예기치 않은 참조 유형을 다른 유형의 변수에 채워서 런타임의 일부를 혼동하지 마십시오. – usr

+1

그냥 토마스를 의심하는 자신을 위해 그것을 시도하십시오. –

+1

주님께서는 강력한 실행 시간의 길을 짐작하게 도와줍니다. – usr