2012-11-08 2 views
8

나는 약간의 연구를 수행했지만, 왜 아직도이 오류가 발생했는지에 관해서 당황하고 있습니다. 나는 다음과 같은 속성을 가진 구조체가 :주소를 가져 오거나, 크기를 가져 오거나, 관리되는 유형을 가리키는 포인터를 선언 할 수 없습니다.

struct Account 
{ 
    //private attributes 
    private double mBalance; 
    private int mAccountNumber; 
    private string mName; 
    private string mDateCreated; 
} 

을하고 다음 작업을 수행하려고 :

class BankManager 
{ 
    //private attributes 
    private unsafe Account *mAccounts; 
    private unsafe bool *mAccountsAvailable; 
    private int mNumberAccounts; 
} 

심지어 클래스의 속성에 대한 "안전하지 않은"사용하여 구조체에 내 수업 계정을 켠 후에 BankManager, 컴파일러가 안전하지 않은 코드를 사용할 수 있습니다 이야기 (속성 -> 빌드), 난 아직도 이유에

*mAccounts 

에서 어떤 아이디어이 오류를 받고 있어요? 나는 구조체에서 사용하고있는 모든 타입이 C#에서 포인터를 갖는 것은 합법적이라고 확신한다. 미리 감사드립니다!

+1

왜 포인터를 사용하고 싶습니까? 'BankManager'에는'Account's의'Collection'이있을 것 같습니다. – Xint0

+0

도움이 될 수 있습니다. http://stackoverflow.com/questions/2559384/cannot-take-the-address-of-get-the-size-of-or-declare-a-pointer-to-managed-t – sellmeadog

답변

20

계정 클래스의 문자열로 인해이 문제가 발생합니다. 이유를 이해하려면 가비지 수집기가 작동하는 방식을 이해해야합니다. 그것은 객체에 대한 참조를 추적하여 쓰레기를 발견합니다. mName 및 mDateCreated는 그러한 참조입니다. mBalance 및 mAccountNumber는 이 아니며이 아닌 값 유형입니다. 그리고 가장 중요한 것은 BankManager.mAccounts 필드가 아니라 포인터입니다.

그래서 컴파일러는 가비지 수집기가 이 아니며이 문자열 참조를 볼 수 있음을 알릴 수 있습니다. 이렇게하는 유일한 방법은 mAccount 필드와 그 참조를 거치지 않기 때문입니다.

유일한 방법은 값 유형을 엄격히 제한하는 것입니다. 문자열에 대해이 작업을 수행하는 유일한 방법은 비 관리 대상 메모리에 Marshal.StringToCoTaskMemUni()를 할당하고 IntPtr을 필드에 저장하는 것입니다. 이제 가비지 수집기에서 손이 닿지 않아 이동이 불가능합니다. 이제는 그 문자열을 놓을 의무가 생깁니다.

명백히 실용적이지 않고 누출을 일으키는 경향이 있습니다. C 프로그램에서 흔히 발생하는 문제입니다. 왜 당신이 이것을 추구하는지 모르지만, 객체에 대한 참조가 이미 간단한 포인터이므로 포인터를 직접 사용하여 얻지 못하는 것을 명심하십시오.

+0

자세한 정보 주셔서 감사합니다! – SantasNotReal

0

string은 포인터 참조를 가질 수없는 관리되는 유형이므로 포인터를 가질 수있는 유형을 포함하는 구조체가 잘못되었습니다.

1

사용 private unsafe fixed char mName[126];
문자열은 관리되는 유형이며 고정되지 않은 배열입니다.

+0

좋아요, 나는 문자열이 관리되지만 문자는 허용된다는 것을 이해합니다. 그래서 위의 작업을 수행하면 다음과 같이 나타납니다. public char Name { get {return mName; } 세트 {mName = 값; } } "고정되지 않은 식에 포함 된 고정 크기 버퍼를 사용할 수 없습니다. 고정 문 사용을 시도하십시오." – SantasNotReal

3

문자열은 .NET의 참조 유형이며 struct 포인터에 대해 blittable이 아닙니다. 원하는 값 유형의 목록은 Blittable and Non-Blittable Types을 참조하십시오.

특별한 비즈니스 요구 사항이있는 경우가 아니면 유지 관리 가능성과 일반적인 정성을 위해 관리 메모리를 사용해야합니다.

+0

이 답변은 큰 디자인 문제를 설명 할 때 받아 들인 대답보다 나에게 더 유익했습니다. 내 특정 문제는 int [] 배열 속성을 포함하는 구조체와 관련이 있습니다. blittable이 아니기 때문에 OP와 같은 오류가 발생합니다. –

-1

복사 된 수집기가 주변을 이동할 수 있으므로 관리되는 데이터가 고정 된 위치에 있지 않습니다. 이는 관리되는 박스형 값 유형에도 똑같이 적용됩니다. Managed unboxed value-type은 스택이나 다른 객체 내부에서만 살 수 있습니다. 스택에 고정 된 위치 만 고정됩니다.

계속 유효한 포인터를 사용할 수있는 고정 위치가있는 힙 할당 구조체를 만들려면 비 관리 대상 메모리에 할당해야합니다. 그러나 일단 관리되지 않는 메모리에 할당하면 더 이상 가비지 수집기에서 해당 포인터에 대해 알지 못해 관리되지 않는 포인터 (예 : 문자열을 사용할 수 없음)를 삽입 할 수 없습니다. 압축 할 때 관리 대상을 이동할 때 사용합니다.

[StructLayout(LayoutKind.Sequential, Pack=1)] 
public unsafe struct Account { 
    public int a; 
    public char* mName; 
} 
public class BankManager { 
    private unsafe Account* mAccounts; 
    public unsafe int GetA() { 
     return mAccounts->a; 
    } 
    public unsafe BankManager() { 
     mAccounts = (Account*)Marshal.AllocHGlobal(sizeof(Account)); 
    } 
    unsafe ~BankManager() { 
     if (mAccounts != null) { 
      Marshal.FreeHGlobal((IntPtr)mAccounts); 
      mAccounts = null; 
     } 
    } 
} 

가 여기에 우리가 관리되지 않는 메모리에서 구조체를 할당 한 :

예를 들어,이 할 수있는 유효한 (그러나 반드시 좋지 않다) 것입니다. 이것은 우리가 변경하거나 이동하지 않는다는 것을 알고있는 포인터를 유지할 수있게 해줍니다. 구조체가 끝나면 수동으로 구조체를 해제해야합니다. 관리되지 않는 char * (c 스타일 문자열)이기 때문에 동일한 수동 alloc/free 및 marshalling을 mAccounts-> mName에 수행해야합니다.

구조체가이 코드의 동작을 C- 카운터 파트에 가깝게 만들기 위해 순차적 레이아웃을 포장했습니다. 위의 코드는 일반적으로 특정 구조체를 예상하는 네이티브 C DllImport 진입 점과의 상호 운용성을 수행 할 때만 사용되기 때문에 형세.

관련 문제