C++/CLI .Net Datentypen werden zu ValueType kompiliert?

Hallo liebe Community. Erneut melde ich mich mit einem Problem, welches sich mir nicht erschließen will..
Ich habe mich über das Problem meines letzten Posts informiert und bin nun dabei, Den Code auf mehrere Dateien zu packen, um sie besser handzuhaben..

Es funktioniert alles wunderbar, bis auf die Tatsache, dass sich einige Datentypen aus dem .Net Framework automatisch in ValueType Typen kompiliert werden (falls das so richtig ausgedrückt ist).

Um es zu verdeutlichen:
Aus:
C++:
System::IntPtr^ GetHandle();

wird:
C#:
System.ValueType GetHandle();

Diese Umwandlung find ich mehr als irritierend, vor allem weil ich keinen ValueType als Rückgabewert haben will, sondern IntPtr!
Original (SDLSharp.Net.INative.h):
C++:
namespace SDLSharp
{
	namespace Net
	{
		/// <summary>
		/// Schnittstelle für Objekte, die nicht verwaltete Daten beinhalten.
		/// </summary>
		public interface class INative
		{
			Pointer GetPointer();
			System::IntPtr^ GetHandle();
		};
	};
};

Kompiliertes Ergebnis:
C#:
using System;
namespace SDLSharp.Net
{
	/// <summary>
	/// Schnittstelle für Objekte, die nicht verwaltete Daten beinhalten.
	/// </summary>
	public interface INative
	{
		unsafe void* GetPointer();
		System.ValueType GetHandle();
	}
}

Mach ich da was falsch?

Mfg
JustShinigami
 
Hallo JustShinigami

Genau gleich wie du auch nicht int^ zurückgibst sondern int machst du das auch bei anderen Valuetypen:
C++:
System::IntPtr GetHandle();
 
Hmm, könntest du mir das kurz erklären? C++ cli ist für mich ya relatives Neuland, benutze es nur gelegentlich, wenn ich Strukturen und Funktionen nutzen will, die C# so nicht hat und ich kein Fan von static extern bin. Den Sinn des ^-Operators versteh ich auch nicht.

Mfg
JustShinigami
 
Also relativ simpel ausgedrückt:
In C# passiert das ganze Referenz vs. Value "magisch" unter dem Deckel, du hast weder Einfluss darauf noch ist es irgendwie ersichtlich ob du jetzt by value oder by reference arbeitest (ja, ausgenommen ref und out). Alles ist einfach genau gleich und irgendwer kümmert sich dann darum ob da jetzt eine Referenz oder der effektive Wert gemeint ist.

In C++(/CLI) geht das nicht. Du musst sagen ob eine Variable eine Referenz ist oder nicht. Und genau dazu ist der ^ Operator da. T^ ist eine Referenz auf einen Wert T. Logischerweise können also Referenztypen nur mit ^ verwendet werden, value-Typen hingegen können sowohl als Referenz aber auch als Wert verwendet werden. Da C# aber Referenzen für Valuetypen nicht kennt werden alle Referenzen auf Valuetypen dann in C# als System.ValueType betrachtet, da das selbst dann wieder ein Referenztyp ist.

Zurück zu deinem Beispiel:
Wenn wir in Worten mal ausdrücken was folgende Zeile macht.
C++:
System::IntPtr^ GetHandle();

Sie gibt eine Referenz auf einen IntPtr zurück. Jetzt überleg dir mal wie du in C# eine Referenz auf einen IntPtr zurückgeben würdest und du merkst, dass das unmöglich ist.

Kleiner Exkurs am Rande, um das ganze noch etwas verwirrender zu machen:
Anhand dessen könntest du auf die Idee kommen Hey, wenn System::IntPtr^ heisst Referenz auf einen IntPtr müsste doch das gehen
C++:
void GetHandle(System::IntPtr^ handle);

C#:
IntPtr ptr = new IntPtr(0);
GetHandle(ref ptr);

Leider ist dem nicht so :D. GetHandle wird zu GetHandle(System.ValueType). Es gibt nämlich noch weitere Unterschiede innerhalb von Referenzen. T^ ist nämlich um genau zu sein ein referengezählter Typ, das heisst es ist wie ein anderes Objekt so verwaltet dass die Referenzen auf das Objekt von der CLR gezählt werden und es dann automatisch freigegeben wird. Übergibst du jedoch einen Parameter mit ref an eine Funktion so erstellst du eine nicht-gezählte Referenz auf das Objekt für die Funktion. Diese Referenz dient nur dazu, dass Änderungen das referenzierte Objekt betreffen und nicht eine Kopie davon, bietet allerdings keine Verwaltung der Referenzen.

Wenn du einen ref-Parameter machen willst wird das zu:
C++:
void GetHandle(System::IntPtr% handle);

Nun kannst du ref ptr verwenden.

Erweitern wir den Verwirrmodus noch ein wenig. Nehmen wir nun an du möchtest nicht einen Valuetypen mit ref übergeben sondern einen Referenztypen (sagen wir mal string), dann dürftest du gemäss obiger Erklärung intuitiv auf das kommen:
C++:
void GetHandle(System::String% handle);

Leider ist das aber nicht so. Bei Referenztypen soll ja ref dazu sorgen, dass du dem Referenzobjekt ein anderes Objekt zuweisen kannst und die Referenz ab dann auf dieses Objekt zeigt. Das heisst da sähe es so aus:
C++:
void GetHandle(System::String^% handle);

Und wie bekommst du nun out hin? Geht nur über Attribute in C++/CLI:
C++:
void GetHandle([System::Runtime::InteropServices::OutAttribute] System::String^% handle);

Ich denke du merkst langsam warum viele Leute nicht so gerne mit C++/CLI arbeiten. Nicht weil es schlecht ist, sondern weil man kaum darum herum kommt beinahe unendlich viele Metazeichen einzuführen um alle möglichen Situationen abzudecken.

Grüsse
Cromon
 
Achso, ich verstehe. Klingt einleuchtend. :)
Herzlichen Dank für die ausführliche Erklärung, hab ich wieder was neues gelernt.

Aber in C++ kommt man oft auch nicht drum rum, viele Metazeichen zu verwenden. So wie ich das verstanden hab, ist das "^" theoretisch das "*" für Managed Objects.

Mfg
JustShinigami
 

Neue Beiträge

Zurück