Hallo Community.
Ich fand es war mal an der Zeit, dieses Thema hier genauer zu beleuchten, da es damit doch ab und zu einige Probleme gibt. Ich werde jetzt nicht damit anfangen, aufzuzählen, was man hierbei alles falsch machen könnte, da das Thema doch etwas komplexer ist, als es aussieht.
Ich werde nun zuerst einmal erklären, was bei ByVal und ByRef im Hintergrund überhaupt passiert und dann, welche Folgen das hat.
Zunächst einmal muss ich aber etwas klären, was einigen vermutlich nicht wirklich bewusst ist.
Es gibt im .Net-Framework zwei verschiedene Arten von Typen, die Wertetypen und die Referenztypen. Die Wertetypen sind
Zwischen diesen beiden Arten von Typen besteht ein großer Unterschied. Während die Wertetypen bei einer Zuweisung kopiert werden, zeigen bei Referenztypen nach einer Zuweisung beide auf das selbe Objekt. Ich habe jetzt schon automatisch das Wort "zeigen" verwendet, denn bei Referenztypen arbeitet das Framework automatisch immer mit Zeigern (Pointern), wodurch eben dieser Effekt hervorgerufen wird. Wenn ihr nicht wisst, was ein Pointer ist, dann empfehle ich euch das nachzulesen, da es vermutlich dazu beitragen wird, das nun folgende besser zu verstehen. In Kurzform jetzt hier: Eine normale Variable "ist" das Objekt, ein Pointer ist ein Verweis auf das Objekt.
Beispiel:
Die Ausgabe hiervon ist
Baue ich das jetzt aber so um, dass wir Klassen haben
dann ist die Ausgabe
So, damit hätten wir die Grundlagen geschafft, weiter zum eigentlichen Artikel.
ByVal
Info: Wenn ihr bei einem Parameter weder ByVal noch ByRef angebt, so wird automatisch ByVal angenommen.
Bei ByVal wird der übergebene Wert "By Value" übergeben, das heißt er wird kopiert. Schauen wir uns an, was das für Wertetypen und für Referenztypen zur Folge hat.
Wertetypen werden einfach kopiert, so wie auch bei einer Zuweisung. Das bedeutet, die Variable innerhalb der Methode ist unabhängig von der an die Methode übergebenen Variablen.
Beispiel:
Dies hat als Ausgabe wiederum
Bei Referenztypen ist es schon interessanter. Bei diesen wird nämlich der Pointer kopiert. Aber was heißt das jetzt? Nun das bedeutet, dass man zwei Pointer hat, die aber immer noch auf das selbe Objekt zeigen.
Mache ich also sowas hier:
dann bekomme ich als Ausgabe
Das bedeutet also, dass alle Änderungen, die ich innerhalb der Funktion an dem Objekt mache, sich auch auf das übergebene Objekt auswirken (oder anders ausgedrückt: es sind die selben Objekte!).
Anders sieht es aber aus, wenn ich den Pointer selbst verändere, also der Variablen etwas zuweise:
In diesem Fall zeigt der kopierte Pointer dann auf ein neues Objekt, weshalb die Ausgabe
Beachtet, dass die Zuweisung des Objektes direkt keinen Auswirkungen auf das übergebene Objekt hat. Das liegt einfach daran, dass solange der kopierte Pointer noch auf das selbe Objekt zeigt, wie der übergebene Pointer, manipuliert man auch am selben Objekt (siehe vorheriges Beispiel). Erzeugt man jetzt aber ein neues Objekt und lässt den kopierten Pointer darauf zeigen, dann manipuliert man folglich auch dieses neue Objekt, während der übergebene Pointer immer noch auf das alte zeigt.
ByRef
ByRef übergibt den Wert "By Reference", was soviel heißt wie "mit Pointer", denn bei ByRef wird nochmal ein Pointer für den Wert erstellt, anstatt dass der Wert kopiert wird.
Was sich daraus für Wertetypen ergibt sieht man an diesem Beispiel:
Vielleicht kennt nun ja schon jemand das Ergebnis, auch schon bevor ich ihm jetzt sage, dass die Ausgabe
Aber wie kann das sein? Habe ich nicht oben gesagt, dass Zuweisungen bei Wertetypen nur den Inhalt kopieren und die beiden Variablen somit unabhängig voneinander sind? Das stimmt auch, aber wie wir ja gerade gelernt haben erzeugt ByRef einen Pointer, und somit verhält sich der Wertetyp dann auch wie ein Referenztyp. Ich erstelle nämlich gar keine neue Variable, sondern eigentlich habe ich nur einen Pointer auf die übergebene Variable und manipuliere damit auch an dieser herum.
Was ist nun aber der Vorteil von Byref gegenüber ByVal bei Referenztypen, bei diesen kann ich ja auch schon mit ByVal am originalen Objekt Dinge verändern?
Das ist schon richtig, allerdings gibt es da ja auch noch die Sache, dass man keine neuen Objekte zuweisen kann. Und genau das ist mit ByRef dann möglich.
Aber was macht ByRef da jetzt genau? Ich habe oben ja schon gesagt, dass ByRef immer einen Pointer erstellt. Und da wir schon einen Pointer übergeben bekommen, haben wir danach einen Pointer, der auf einen anderen Pointer zeigt. Keine Sorge, das hört sich jetzt vielleicht verwirrender an, als es eigentlich ist.
Also der Reihe nach. Wenn man nun diesem Pointer auf den Pointer etwas zuweist, dann verändert man ja logischerweise den Pointer, auf den gezeigt wird. Das ist ein Unterschied, denn vorher hatten wir nur zwei Pointer, die auf das selbe Objekt gezeigt haben. Wenn man nun also nicht auf das Objekt, sondern auf den Pointer zugreift, dann ändere ich damit ja auch die übergebene Variable, denn die ist ja dieser Pointer. Wenn ich innerhalb der Funktion also dem Pointer auf den anderen Pointer etwas zuweise, dann ändere ich den übergebenen Pointer, nicht das Objekt, auf das mein Pointer zeigt, und damit greife ich dann auch von außen auf diesen neuen Pointer und damit auch auf das neue Objekt zu.
Zur Verdeutlichung nochmal ein Beispiel:
Dies hat die Ausgabe
Ich hoffe, ich konnt wieder etwas mehr Klarheit bei euch schaffen, und bedanke mich fürs Lesen.
Bei Fragen stehe ich wie immer gerne zur Verfügung.
Ich fand es war mal an der Zeit, dieses Thema hier genauer zu beleuchten, da es damit doch ab und zu einige Probleme gibt. Ich werde jetzt nicht damit anfangen, aufzuzählen, was man hierbei alles falsch machen könnte, da das Thema doch etwas komplexer ist, als es aussieht.
Ich werde nun zuerst einmal erklären, was bei ByVal und ByRef im Hintergrund überhaupt passiert und dann, welche Folgen das hat.
Zunächst einmal muss ich aber etwas klären, was einigen vermutlich nicht wirklich bewusst ist.
Es gibt im .Net-Framework zwei verschiedene Arten von Typen, die Wertetypen und die Referenztypen. Die Wertetypen sind
Byte, SByte, Short, UShort, Integer, UInteger, Long, ULong, Single, Double, Decimal, Char
, und String
zähle ich hier jetzt auch mal dazu, weil er sich so verhält wie ein Wertetyp (String ist ein Sonderfall, aber das zu erklären ist nicht Aufgabe dieses Artikels). Weitere Wertetypen sind alle Strukturen und alle Enumerationen. Die Referenztypen hingegen werden durch Klassen repräsentiert.Zwischen diesen beiden Arten von Typen besteht ein großer Unterschied. Während die Wertetypen bei einer Zuweisung kopiert werden, zeigen bei Referenztypen nach einer Zuweisung beide auf das selbe Objekt. Ich habe jetzt schon automatisch das Wort "zeigen" verwendet, denn bei Referenztypen arbeitet das Framework automatisch immer mit Zeigern (Pointern), wodurch eben dieser Effekt hervorgerufen wird. Wenn ihr nicht wisst, was ein Pointer ist, dann empfehle ich euch das nachzulesen, da es vermutlich dazu beitragen wird, das nun folgende besser zu verstehen. In Kurzform jetzt hier: Eine normale Variable "ist" das Objekt, ein Pointer ist ein Verweis auf das Objekt.
Beispiel:
2, 1
, da b von der Zuweisung danach nicht betroffen ist, da eben nur der Wert kopiert wurde.Baue ich das jetzt aber so um, dass wir Klassen haben
2, 2
, denn a und b sind durch die Zuweisung Pointer auf das selbe Objekt und greifen damit auch auf das selbe Objekt zu.So, damit hätten wir die Grundlagen geschafft, weiter zum eigentlichen Artikel.
ByVal
Info: Wenn ihr bei einem Parameter weder ByVal noch ByRef angebt, so wird automatisch ByVal angenommen.
Bei ByVal wird der übergebene Wert "By Value" übergeben, das heißt er wird kopiert. Schauen wir uns an, was das für Wertetypen und für Referenztypen zur Folge hat.
Wertetypen werden einfach kopiert, so wie auch bei einer Zuweisung. Das bedeutet, die Variable innerhalb der Methode ist unabhängig von der an die Methode übergebenen Variablen.
Beispiel:
2, 1
.Bei Referenztypen ist es schon interessanter. Bei diesen wird nämlich der Pointer kopiert. Aber was heißt das jetzt? Nun das bedeutet, dass man zwei Pointer hat, die aber immer noch auf das selbe Objekt zeigen.
Mache ich also sowas hier:
2, 2
.Das bedeutet also, dass alle Änderungen, die ich innerhalb der Funktion an dem Objekt mache, sich auch auf das übergebene Objekt auswirken (oder anders ausgedrückt: es sind die selben Objekte!).
Anders sieht es aber aus, wenn ich den Pointer selbst verändere, also der Variablen etwas zuweise:
2, 1
lautet.Beachtet, dass die Zuweisung des Objektes direkt keinen Auswirkungen auf das übergebene Objekt hat. Das liegt einfach daran, dass solange der kopierte Pointer noch auf das selbe Objekt zeigt, wie der übergebene Pointer, manipuliert man auch am selben Objekt (siehe vorheriges Beispiel). Erzeugt man jetzt aber ein neues Objekt und lässt den kopierten Pointer darauf zeigen, dann manipuliert man folglich auch dieses neue Objekt, während der übergebene Pointer immer noch auf das alte zeigt.
ByRef
ByRef übergibt den Wert "By Reference", was soviel heißt wie "mit Pointer", denn bei ByRef wird nochmal ein Pointer für den Wert erstellt, anstatt dass der Wert kopiert wird.
Was sich daraus für Wertetypen ergibt sieht man an diesem Beispiel:
2, 2
ist?Aber wie kann das sein? Habe ich nicht oben gesagt, dass Zuweisungen bei Wertetypen nur den Inhalt kopieren und die beiden Variablen somit unabhängig voneinander sind? Das stimmt auch, aber wie wir ja gerade gelernt haben erzeugt ByRef einen Pointer, und somit verhält sich der Wertetyp dann auch wie ein Referenztyp. Ich erstelle nämlich gar keine neue Variable, sondern eigentlich habe ich nur einen Pointer auf die übergebene Variable und manipuliere damit auch an dieser herum.
Was ist nun aber der Vorteil von Byref gegenüber ByVal bei Referenztypen, bei diesen kann ich ja auch schon mit ByVal am originalen Objekt Dinge verändern?
Das ist schon richtig, allerdings gibt es da ja auch noch die Sache, dass man keine neuen Objekte zuweisen kann. Und genau das ist mit ByRef dann möglich.
Aber was macht ByRef da jetzt genau? Ich habe oben ja schon gesagt, dass ByRef immer einen Pointer erstellt. Und da wir schon einen Pointer übergeben bekommen, haben wir danach einen Pointer, der auf einen anderen Pointer zeigt. Keine Sorge, das hört sich jetzt vielleicht verwirrender an, als es eigentlich ist.
Also der Reihe nach. Wenn man nun diesem Pointer auf den Pointer etwas zuweist, dann verändert man ja logischerweise den Pointer, auf den gezeigt wird. Das ist ein Unterschied, denn vorher hatten wir nur zwei Pointer, die auf das selbe Objekt gezeigt haben. Wenn man nun also nicht auf das Objekt, sondern auf den Pointer zugreift, dann ändere ich damit ja auch die übergebene Variable, denn die ist ja dieser Pointer. Wenn ich innerhalb der Funktion also dem Pointer auf den anderen Pointer etwas zuweise, dann ändere ich den übergebenen Pointer, nicht das Objekt, auf das mein Pointer zeigt, und damit greife ich dann auch von außen auf diesen neuen Pointer und damit auch auf das neue Objekt zu.
Zur Verdeutlichung nochmal ein Beispiel:
2, 2
, denn durch das ByRef ändere ich bei der Zuweisung in Zeile 11 nicht nur meinen Pointer, sondern auch den übergebenen Pointer. Also eigentlich sind somit a und value in der Methode die selben Objekte, wie als hätte ich eine einfache a = b Zuweisung von Referenztypen (wie oben gezeigt) getätigt.Ich hoffe, ich konnt wieder etwas mehr Klarheit bei euch schaffen, und bedanke mich fürs Lesen.
Bei Fragen stehe ich wie immer gerne zur Verfügung.
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Artentus“ ()