Jochens Weblog

ESCde Developer Blog

  Home :: Kontakt :: RSS Feed
  12 Posts :: 0 Artikel :: 4 Kommentare :: 0 Trackbacks

Archiv

Post Kategorien

ESCde

ESCde Blogger

Mittwoch, 13. Juni 2007 #

Als erprobter C#-Entwickler kennt man natürlich das Konzept des Boxing/Unboxing: Struct-Instanzen liegen üblicherweise auf dem Stack. Möchte man allerdings

Methoden auf diesen Instanzen ausführen (Beispielsweise die "ToString()"-Methode), so muss man das Struct zunächst in einen Referenztyp verwandeln: Die

Struct-Instanz wird auf den Heap verschoben und es wird auf dem Stack eine Referenz darauf angelegt. Beispielsweise so:

struct MyStruct {}

MyStruct ms; // Instanz von MyStruct auf dem Stack erstellen
object obj = (object) ms; // ms wird auf den Heap kopiert, obj verweist auf dieses Objektms.ToString();
// nun kann ToString() aufgerufen werden

Soweit nichts neues. Allerdings gibt es auch Situationen, in denen das Geschehen nicht so offensichlich ist wie hier. Betrachten Sie die folgenden

CodeZeilen:

struct MyStruct
{
    private int i;
    public void SetI(int x) { i = x; };
}

public static void Main()
{
    MyStruct ms;
    ms.SetI(1);
    object obj = (object) ms;
    ((MyStruct)obj).SetI(5); // [1]
}

In diesem Codeausschnitt wird ein Element vom Typ MyStruct erzeugt, welches einen privaten Integer sowie eine öffentliche Methode hat, um diesen zu setzen.

In der Main-Methode wird dieser zuerst auf den Wert 1 gesetzt, anschließend wird das Objekt auf den Heap geboxt. Interessant wird es in der mit [1]

markierten Zeile: Hier konvertieren wir obj nach MyStruct, um anschließend mittels SetI() den Wert des structs zu ändern. Auf den ersten Blick wirkt es so,

als würde die Objektreferenz obj in eine MyStruct-Referenz konvertiert und SetI(8) somit auf dem Objekt auf dem Heap ausgeführt. Aber weit gefehlt: Da

MyStruct nach wie vor ein Wertetyp ist (und eben kein Referenz-Typ) wird in dieser Situation Unboxing durchgeführt: Das von obj referenzierte Objekt wird

zurück auf den Stack kopiert und SetI auf dieses Element durchgeführt. Anschließend wird dieses Objekt wieder gelöscht (da wir den Wert nicht in einer

Variablen abspeichern). Die auf dem Heap liegende MyStruct-Instanz, auf die obj verweist, hat also auch nach dem SetI(8)-Aufruf noch den Wert 1 in i

gespeichert.

Quintessenz des Ganzen ist, dass structs, die auf den Heap geboxt wurden nicht veränderbar sind. Bei einer Konversion in einen Struct-Datentyp wird immer

unboxing aufgerufen, die Objekte auf dem Heap werden hierbei nicht angerührt, auch dann nicht, wenn die Syntax zunächst anderes vermuten lässt.

Aber Vorsicht: eine Variante gibt es doch noch, um Struct-Elemente auf dem Heap zu bearbeiten: Interfaces. Betrachten Sie die folgende Variante des obigen

Codes:

Interface I { void SetI(int x); }

struct MyStruct : I
{
    private int i;
    public void SetI(int x) { i = x; };
}

public static void Main()
{
    MyStruct ms;
    ms.SetI(1);
    Object obj = (object) ms;
    ((I)obj).SetI(5); // [2]
}

Nun wird die SetI()-Methode von einem Interface angeboten. MyStruct implementiert dieses Interface. Wen wir nun wie in Zeile [2] unsere Objekt-Referenz nicht

nach MyStruct sondern nach I konvertieren, so "trifft" unser SetI(5)-Aufruf wirklich das Objekt auf dem Heap, ohne eine Kopie zu erstellen. Dies liegt daran,

dass Interfaces immer Referenztypen sind. Daher kommt der Cast von Object nach Interface auch ohne Unboxing aus.