2012-11-08 10 views
8

Ich habe ein gutes Stück Forschung, aber bin jetzt stecken, warum ich immer noch diesen Fehler bekomme. Ich habe eine Struktur mit den folgenden Attributen:Kann nicht die Adresse von, nehmen Sie die Größe von, oder einen Zeiger auf einen verwalteten Typ zu deklarieren

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

und ich versuche, die folgend zu tun:

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

Auch nach meiner Klasse Account an eine Struktur drehen, „unsicher“ für die Attribute in der Klasse mit Bankmanager, und sagt den Compiler kann unsicheren Code verwenden (in Eigenschaften -> Build), ich bin immer noch diesen Fehler bei

*mAccounts 

Irgendwelche Ideen, warum? Ich bin ziemlich sicher, dass alle Typen, die ich in der Struktur verwende, legal sind, Zeiger in C# zu haben. Danke im Voraus!

+1

Warum möchten Sie Zeiger verwenden? Es sieht so aus, als hätte BankManager eine 'Sammlung' von' Konten'. – Xint0

+0

Dies kann helfen: http://stackoverflow.com/questions/2559384/cannot-the-address-of-get-the-size-of-or-declare-a-pointer-to-a-managed-t – sellmeadog

Antwort

20

Die Zeichenfolgen in der Klasse Account verursachen dieses Problem. Um zu verstehen, warum, müssen Sie verstehen, wie der Garbage Collector funktioniert. Es entdeckt Müll, indem Verweise auf Objekte verfolgt werden. Die mName und mDateCreated sind solche Referenzen. Die mBalance und die mAccountNumber sind nicht, diese Felder sind Werttypen. Und am wichtigsten ist das Feld BankManager.mAccounts nicht, es ist ein Zeiger.

So kann der Compiler vorne sagen, dass der Garbage Collector nie in der Lage sein wird, die String-Referenzen zu sehen. Weil der einzige Weg dies zu tun ist, durch das mAccount-Feld zu gehen, und es ist keine Referenz.

Das einzige Heilmittel dafür ist, sich streng auf Werttypen zu beschränken. Die einzige Möglichkeit, dies für Zeichenketten zu tun, ist sie in unmanaged Speicher mit, sagen wir, Marshal.StringToCoTaskMemUni() und speichern Sie die IntPtr in das Feld. Es ist jetzt außerhalb der Reichweite des Müllsammlers und kann nicht von ihm bewegt werden. Sie werden nun auch die Last haben, diese Zeichenfolge freizugeben.

Natürlich ist das nicht praktisch und anfällig für Lecks, die Art von Problem, die in C-Programmen so häufig ist. Nicht sicher, warum Sie dies überhaupt verfolgen, aber bedenken Sie, dass ein Verweis auf ein Objekt bereits ein einfacher Zeiger ist, so dass Sie nichts gewinnen, wenn Sie selbst Zeiger verwenden.

+0

Danke für die detaillierte Info! – SantasNotReal

0

Sie liegen falsch bezüglich der Struktur, die Typen enthält, die Zeiger haben können, weil string ein verwalteter Typ ist, der keine Zeigerreferenz haben kann.

1

Verwendung private unsafe fixed char mName[126];
Strings sind verwaltete Typen, also nicht fixierte Arrays.

+0

Ok, ich verstehe, dass Strings verwaltet werden, aber Zeichen sind erlaubt. Also wenn ich das obige mache, bekomme ich: public char Name { erhalten {return mName; } set {mName = Wert; } } mit dem Fehler "Sie können keine Puffer fester Größe verwenden, die in unfixierten Ausdrücken enthalten sind. Verwenden Sie die feste Anweisung." – SantasNotReal

3

Zeichenfolgen sind Referenztypen in .NET und sind für Strukturzeiger nicht blitbar. Unter Blittable and Non-Blittable Types finden Sie eine Liste der Wertetypen für das, was Sie tun möchten.

Sofern Sie keine besonderen geschäftlichen Anforderungen haben, sollten Sie bei der Verwaltung und Wartung auf dem verwalteten Speicher bleiben.

+0

Diese Antwort war für mich vorteilhafter als die akzeptierte Antwort, da sie das größere Designproblem beschreibt. Mein spezielles Problem bezog sich auf eine Struktur mit einer Array-Eigenschaft int [], die nicht blittierbar ist und zu demselben Fehler führt wie das OP. –

-1

Verwaltete Daten bleiben nicht an einem festen Ort, da der Kopiersammler Dinge verschieben kann. Dies gilt gleichermaßen für verwaltete Boxed-Value-Typen. Verwaltete nicht verpackte Werttypen können nur auf dem Stapel oder in anderen Objekten leben. Sie haben nur feste Positionen, wenn sie sich im Stapel befinden.

Um eine Heap-allokierte Struktur zu erstellen, die einen festen Ort hat, von dem Sie einen Zeiger nehmen können, der weiterhin gültig ist, müssen Sie ihn in unmanaged Speicher reservieren. Allerdings, sobald Sie es im nicht verwalteten Speicher zuweisen, können Sie nicht mehr verwaltete Zeiger darin (aka, können Sie nicht verwenden Zeichenfolge), weil der Garbage Collector nicht über diese Zeiger wissen, so wird es nicht aktualisiert werden wenn sie verwaltete Objekte während der Komprimierung verschieben.

Zum Beispiel ist dies ein gültiges (wenn auch nicht unbedingt gut), was zu tun:

[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; 
     } 
    } 
} 

Hier haben wir die Struktur in nicht verwalteten Speichern zugewiesen haben. Dies ermöglicht uns, einen Zeiger darauf zu halten, von dem wir wissen, dass er sich nicht ändert oder bewegt. Wir müssen die Struktur manuell freigeben, wenn wir damit fertig sind. Das selbe manuelle alloc/free und marshalling müsste an mAccounts-> mName vorgenommen werden, da es sich um ein nicht verwaltetes char * (c-style string) handelt.

Ich habe die Struktur gepackt sequenzielles Layout, um das Verhalten dieses Codes näher zu seinem C-Gegenstück zu machen, weil Code wie oben nur bei Interop mit einem nativen C DllImport Einstiegspunkt, der eine bestimmte Struktur erwartet verwendet wird Layout.