|
| » myCSharp.de Diskussionsforum |
|
|
|
|
Autor
 |
|
Vertexwahn
myCSharp.de-Mitglied
Dabei seit: 25.10.2005
Beiträge: 179
Entwicklungsumgebung: Visual Studio 2008
 |
|
C# und Übergabemechanismen: call by value vs. call by reference
Motivation
Wissen Sie wie call by value und call by reference in C# funktioniert? Was…? Sie sind ein alter C++ Programmierer und in C# ist doch alles genau so? Hier ein Motivationsbeispiel:
C#-Code: |
using System;
class Auto
{
public Auto(int AnzahlReifen)
{
m_AnzahlReifen = AnzahlReifen;
}
public int m_AnzahlReifen;
}
public class MyApp
{
public static void Main()
{
Auto MeinAuto = new Auto(4);
foo(MeinAuto);
System.Console.WriteLine(MeinAuto.m_AnzahlReifen);
}
static void foo(Auto irgendeinAuto)
{
irgendeinAuto = new Auto(6);
}
}
|
Wenn sie jetzt verwundert vor dem Bildschirm hocken und sich fragen warum obiges Programm die Zahl 4 ausgibt und nicht die Zahl 6, dann sind Sie ein Leser meine Zielgruppe. Falls nicht, werden sie im folgendem auch nichts neues mehr erfahren.
Alles call by value
Um es kurz zu machen: Parameterübergabe in C# erfolgt implizit immer in Form von call by value.
Jetzt fragen Sie sich sicherlich warum folgendes geht und warum das nicht call by reference ist:
C#-Code: |
using System;
class Auto
{
public Auto(int AnzahlReifen)
{
m_AnzahlReifen = AnzahlReifen;
}
public int m_AnzahlReifen;
}
public class MyMain
{
public static void Main()
{
Auto MeinAuto = new Auto(4);
foo(MeinAuto);
System.Console.WriteLine(MeinAuto.m_AnzahlReifen);
}
static void foo(Auto irgendeinAuto)
{
irgendeinAuto.m_AnzahlReifen = 6;
}
}
|
Die Erklärung ist ganz einfach. Übergibt man eine Referenz an eine Methode, so wird ihr Inhalt kopiert. Was ist der Inhalt eine Referenz? Der Inhalt einer Referenz ist nur ein Verweis, der angibt, wo sich das zugehörige Objekt befindet. Wenn man also eine Referenz an eine Methode übergibt wird eine Kopie der Referenz an die Funktion übergeben.
C#-Code: |
Auto A = new Auto();
doSomething(A);
void doSomething(Auto B)
{
}
|
Bei dem Aufruf der Methode doSomething wird eine zweite Referenz angelegt, die auf das Objekt Auto verweist. Verändert die Referenz B das Objekt so wirkt sich dies auf das Auto Objekt aus.
Man kann in der Methode doSomething das Auto Objekt verändern, aber nicht die Referenz A.
Sehen Sie sich jetzt noch einmal das Motivationsbeispiel an und stellen Sie fest warum 4 und nicht 6 ausgegeben wird.
Man kann in der Methode doSomething nicht die Referenz A auf ein anderes Objekt zeigen lassen, da man nur mit einer Kopie arbeitet.
C#-Code: |
Auto A = new Auto();
doSomething(A);
void doSomething(Auto B)
{
B = new Aute();
}
|
Es bleibt festzuhalten: C# arbeitet mit call-by-value, auch wenn man Referenzen übergibt. Bei Java ist es genau so.
Call by refernece
In C# ist auch call by reference bei der Paramterübergabe möglich. Dabei wird vor jedem Parameter der per call by reference übergeben werden soll das Schlüsselwort ref gestellt:
C#-Code: |
using System;
class Auto
{
public Auto(int AnzahlReifen)
{
m_AnzahlReifen = AnzahlReifen;
}
public int m_AnzahlReifen;
}
public class MyMain
{
public static void Main()
{
Auto MeinAuto = new Auto(4);
foo(ref MeinAuto);
System.Console.WriteLine(MeinAuto.m_AnzahlReifen);
}
static void foo(ref Auto irgendeinAuto)
{
irgendeinAuto = new Auto(6);
}
}
|
Das Schlüsselwort out
Häufig besitzen Methoden Ergebnisparameter. Ergebnisparameter sind Parameter, die nur auf der linken Seite einer Anweisung auftreten. Ihr Wert vor der Ausführung der Methode ist also bedeutungslos. Beispiel:
C#-Code: |
using System;
class MyClass
{
static void Main(string[] args)
{
int ergebnis = 0;
add(3, 2, ref ergebnis);
System.Console.WriteLine(ergebnis);
}
static void add(int x, int y, ref int ergebnisparameter)
{
ergebnisparameter = x + y;
}
}
|
Der Wert von ergebnis ist bedeutungslos, da dieser sowieso mit dem Wert 5 (x+y) überschrieben wird. Die Initialisierung der Variablen ergebnis mit einem bestimmten Wert (z. B. 0) ist unsinnig, da das Ergebnis von x und y abhängig ist. Lässt man die Initialisierung weg, bekommt man eine Fehlermeldung der Art „Verwendung einer nicht zugewiesener lokalen Variablen“:
C#-Code: |
using System;
class MyClass
{
static void Main(string[] args)
{
int ergebnis;
add(3, 2, ref ergebnis);
System.Console.WriteLine(ergebnis);
}
static void add(int x, int y, ref int ergebnisparameter)
{
ergebnisparameter = x + y;
}
}
|
Abhilfe schafft hier das Schlüsselwert out, welches einen Parameter als Ergebnisparameter kennzeichnet. Parameter, die mit out gekennzeichnet sind, müssen nicht initialisiert werden:
C#-Code: |
using System;
class MyClass
{
static void Main(string[] args)
{
int ergebnis;
add(3, 2, out ergebnis);
System.Console.WriteLine(ergebnis);
System.Console.ReadLine();
}
static void add(int x, int y, out int ergebnisparameter)
{
ergebnisparameter = x + y;
}
}
|
Grundsätzlich ist es guter Stil Variablen gleich zu initialisieren, damit man davor bewahrt wird eine nicht initialisierte Variable zu verwenden. Ein Compiler, der eine Warnung bei benutzen einer nicht initialisierten Variablen ausspuckt, ist ein wahrer Segen. Mit bedacht kann man aber das Schlüsselwort out ruhigen Gewissens einsetzen.
Ein weiterer Vorteil des out Parameters ist die Rückgabe von mehreren Ergebnissen. Natürlich kann man dies auch mit Rückgabewerten von Methoden erreichen, jedoch ist man hier auf einen einzelnen Rückgabewert beschränkt. Eine Methode kann nicht zwei Werte zurückgeben. In solchen Fällen kann man zwar überlegen, ob es nicht sinnvoll ist beide Rückgabeparameter zu einen neuen Typ zusammenfassen und dadurch einen einzelnen Rückgabewert zu schaffen, jedoch gibt es Situationen, in denen eine Zusammenfassung aufgrund der Semantik der Daten einfach nicht sinnvoll ist, da die Daten logisch nicht zusammengehören oder es ist einfach bequemer zwei „Rückgabewerte“ bereitzustellen.
Beispiel:
C#-Code: |
private void getProxySettings(out string IP, out int Port)
{
}
|
Schlusswort
Ich hoffe der Unterschied zwischen call by value und call by reference ist klar geworden. Der direkte Zugriff auf als public Elemente, wie in den obigen Beispielen gezeigt (MeinAuto.m_AnzahlReifen) ist natürlich kein sauberer Stil. Die Art des Zugriffs auf public Elemente, wurde gewählt um den Umfang der Beispielprogramme möglichst gering und übersichtlich zu halten. Anstelle von direktem Zugriff auf Datenelemente sollte man so genannte Getter- und Settermethoden benutzen. In C# sind diese als Properties bekannt. Wer diese nicht schon lange kennt, sollte danach mal googeln.
Dieser Beitrag wurde 9 mal editiert, zum letzten Mal von Vertexwahn am 29.11.2005 19:21.
|
|
18.11.2005 22:54 |
E-Mail |
Website |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
herbivore
myCSharp.de-Team (Admin)

Dabei seit: 11.01.2005
Beiträge: 42.184
Entwicklungsumgebung: csc/nmake (nothing is faster) Herkunft: Berlin
 |
|
Hallo Vertexwahn,
ich finde deinen Artikel gut. Nette Einleitung, gute Motivation.
Du solltest allerdings 'out' noch in dem Artikel erklären, ist ja schnell gemacht. Zumal man, wenn man bei google nur nach 'out' sucht, nicht wirklich fündig wird.
Was mir auch nicht gefällt ist das public Field m_AnzahlReifen. Gerade wenn Anfänger den Artikel lesen, werden die dann auf eine falsche Fährte geführt. Ich weiß aber leider auch keine richtig Lösung, weil die an sich sinnvollen Properties hier das Verständnis vermutlich erschweren. Du könntest aber zumindest in einem Nachwort klarstellen, dass man public Fields meiden sollte.
herbivore
|
|
18.11.2005 23:25 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Vertexwahn
myCSharp.de-Mitglied
Dabei seit: 25.10.2005
Beiträge: 179
Entwicklungsumgebung: Visual Studio 2008
Themenstarter
 |
|
| Zitat: |
Original von herbivore
Du solltest allerdings 'out' noch in dem Artikel erklären, ist ja schnell gemacht. Zumal man, wenn man bei google nur nach 'out' sucht, nicht wirklich fündig wird.
Was mir auch nicht gefällt ist das public Field m_AnzahlReifen
herbivore |
Habe die Änderungen vorgenommen!
|
|
20.11.2005 18:26 |
E-Mail |
Website |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
herbivore
myCSharp.de-Team (Admin)

Dabei seit: 11.01.2005
Beiträge: 42.184
Entwicklungsumgebung: csc/nmake (nothing is faster) Herkunft: Berlin
 |
|
Hallo Vertexwahn,
fein! Wird immer besser. Vielleicht könntest du noch eine Antwort auf die Frage "Warum soll ich out benutzen, wenn ich ebenso gut einen Rückgabewert nehmen kann?" geben, die man sich bei dem konkreten Beispiel leicht stellen kann.
Dann musst du eigentlich nur noch den Titel ändern und einen Admin dazu bringen, den Beitrag ins Forum Artikel zu verschieben.
herbivore
|
|
20.11.2005 18:39 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Vertexwahn
myCSharp.de-Mitglied
Dabei seit: 25.10.2005
Beiträge: 179
Entwicklungsumgebung: Visual Studio 2008
Themenstarter
 |
|
| Zitat: |
| Warum soll ich out benutzen, wenn ich ebenso gut einen Rückgabewert nehmen kann? |
weil es nicht 2 Rückgabewerte gibt
mmh... soll ich das wirklich noch dazu schreiben
|
|
29.11.2005 11:03 |
E-Mail |
Website |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
herbivore
myCSharp.de-Team (Admin)

Dabei seit: 11.01.2005
Beiträge: 42.184
Entwicklungsumgebung: csc/nmake (nothing is faster) Herkunft: Berlin
 |
|
Hallo Vertexwahn,
| Zitat: |
| soll ich das wirklich noch dazu schreiben |
ja :-)
Das Beispiel ist doch auch gut.
herbivore
|
|
29.11.2005 17:17 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Programmierhans
myCSharp.de-Poweruser/ Experte

Dabei seit: 05.04.2005
Beiträge: 3.696
Entwicklungsumgebung: VS 2003 Prof. / VS 2008 Prof. Herkunft: Zentralschweiz
 |
|
Was für einen FAQ-Artikel noch fehlt ist der folgende Hinweis:
Obwohl in C# ein ref-Parameter inizialisiert werden muss damit er übergeben werden kann sollte man in DLL's (welche in verschiedenen Sprachen verwendet werden können) trotzdem den Wert überprüfen bevor er verwendet wird... dies aus folgenden Gründen:
a) ein Wert kann mit null inizialisiert werden (geht in allen Sprachen)
b) Andere .Net Sprachen unterscheiden nicht zwischen ref und out.... so ist es z.B: in VB.Net problemlos möglich mit ByRef einen nicht inizialisierten Wert an einen ref-Parameter zu übergeben (die Prüfung in VB entfällt, da VB keine Unterscheidung zwischen ref und out kennt).
|
|
29.11.2005 19:59 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
| Zwischen diesen beiden Beiträgen liegen mehr als 2 Monate. |
herbivore
myCSharp.de-Team (Admin)

Dabei seit: 11.01.2005
Beiträge: 42.184
Entwicklungsumgebung: csc/nmake (nothing is faster) Herkunft: Berlin
 |
|
Hallo zusammen,
der Artikel steht jetzt in 'Artikel'. :-)
herbivore
|
|
21.02.2006 19:32 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Bernhard
myCSharp.de-Mitglied
Dabei seit: 17.06.2005
Beiträge: 119
 |
|
| Zitat: |
| Wissen Sie wie call by value und call by reference in C# funktioniert? Was…? Sie sind ein alter C++ Programmierer und in C# ist doch alles genau so? |
Nur so als kleine Anmerkung, nachdem ich auch von C++ gewechselt bin: Es funktioniert in der Tat genau so. Auch in C++ wird alles, sofern kein & dabei steht, per Value übergeben, also bei "Wertetypen" eben der Inhalt selbst und bei Zeigern eben die Adresse. Das Äquivalent zum ref Schlüsselwort in C# ist dann eben das & nach dem Typ des Parameters.
C#-Code: |
void foo1(Auto* auto) {
auto = new Auto(6);
}
void foo2(Auto* auto) {
auto->setAnzahlReifen(6);
}
void foo3(Auto*& auto) {
auto = new Auto(6);
}
|
grüße
|
|
26.02.2006 18:23 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Vertexwahn
myCSharp.de-Mitglied
Dabei seit: 25.10.2005
Beiträge: 179
Entwicklungsumgebung: Visual Studio 2008
Themenstarter
 |
|
| Zitat: |
| Es funktioniert in der Tat genau so |
Du hast Recht.
Hier der C++ Code zum Testen im Compiler:
C#-Code: |
#include <iostream>
using namespace std;
class Auto
{
public:
Auto(int AnzahlReifen)
{
m_AnzahlReifen = AnzahlReifen;
}
int m_AnzahlReifen;
};
void foo1(Auto* a) {
a = new Auto(6);
}
void foo2(Auto* b) {
b->m_AnzahlReifen = 6;
}
void foo3(Auto*& c) {
c = new Auto(6);
}
int main()
{
Auto x(4);
foo1(&x);
cout<<x.m_AnzahlReifen<<endl;
foo2(&x);
cout<<x.m_AnzahlReifen<<endl;
x.m_AnzahlReifen = 4;
Auto *ptr = &x;
foo3(ptr);
cout<<x.m_AnzahlReifen<<endl;
cout<<ptr->m_AnzahlReifen<<endl;
cin.get();
}
|
|
|
27.02.2006 11:24 |
E-Mail |
Website |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
| Zwischen diesen beiden Beiträgen liegen mehr als 6 Monate. |
wdb.lizardking
myCSharp.de-Mitglied

Dabei seit: 28.08.2006
Beiträge: 100
Entwicklungsumgebung: Visual Studio .NET 2008 Herkunft: Niederbayern
 |
|
Danke für den guten Artikel.
Für einen Umsteiger von C++ wie mich war er sehr hilfreich.
|
|
28.08.2006 10:11 |
E-Mail |
Website |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
herbivore
myCSharp.de-Team (Admin)

Dabei seit: 11.01.2005
Beiträge: 42.184
Entwicklungsumgebung: csc/nmake (nothing is faster) Herkunft: Berlin
 |
|
Hallo Moooitic,
im Prinzip hast du recht, nur ist das kein spezifisches Problem von out, sondern ist bei der Verwendung von return ganz genauso zu beachten.
| Zitat: |
| Ihr solltet definitiv erwähnen das es beim out-Schlüsselwort einzig und allein um einen geschwindigkeitsvorteil geht. |
Nö, das würde ich nicht sagen. Die TryParse-Methoden sind ein gutes Beispiel für die sinnvolle und oo-konforme Verwendung von out.
herbivore
|
|
21.03.2007 06:56 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Borg
myCSharp.de-Poweruser/ Experte
Dabei seit: 23.08.2006
Beiträge: 1.529
Entwicklungsumgebung: VS2005 Herkunft: Berlin, Germany
 |
|
| Zitat: |
| Die TryParse-Methoden sind ein gutes Beispiel für die sinnvolle und oo-konforme Verwendung von out. |
Nun ja, soweit würde ich nicht gehen. TryParse hat ja auch bloß das Problem zweier Rückgabewerte.
Prinzipiell sind out-Parameter wirklich (oo-gesehen) überflüssig. Oo wäre es, wenn Parse ein entsprechendes Objekt zurückgibt. Allerdings macht das die Schreibweise oftmals komplizierter.
C#-Code: |
int myInt;
if (int.TryParse( myString, myInt))
{
}
int.ParseResult ipr = int.Parse( myString );
if (ipr.Successful)
{
int myInt = ipr.Value;
}
|
|
|
21.03.2007 13:42 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
| Zwischen diesen beiden Beiträgen liegen mehr als 3 Monate. |
chrische5
myCSharp.de-Mitglied
Dabei seit: 02.07.2007
Beiträge: 489
Entwicklungsumgebung: Visual Studio 2005 Herkunft: Europa
 |
|
Hallo
| Zitat: |
Original von Vertexwahn
C#-Code: |
Auto A = new Auto();
doSomething(A);
void doSomething(Auto B)
{
B = new Aute();
}
|
|
Das soll sicher
C#-Code: |
b= new Auto();
|
heißen. Soll nicht nerven, sondern nur mein erster Beitrag zur Community sein.
Sonst toller Artikel, der mir als c++er sehr geholfen hat. Gibt es eigentlich eine
Entsprechung für const in c#?
chrische
|
|
04.07.2007 14:57 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
| Zwischen diesen beiden Beiträgen liegen mehr als 11 Monate. |
MrLeeh
myCSharp.de-Mitglied

Dabei seit: 19.02.2008
Beiträge: 194
Entwicklungsumgebung: Visual Studio Herkunft: Deutschland
 |
|
Sehr guter Artikel. Der bringt endlich mal Licht ins Dunkel der ByValue und ByReference Geschichte. Da denkt man, man hat schon lange alles verstanden und auf einmal war es doch ganz anders. Vielen Dank für die Mühe.
|
|
21.06.2008 18:36 |
E-Mail |
Website |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
winSharp93
myCSharp.de-Team (Moderation)

Dabei seit: 19.01.2007
Beiträge: 3.870
Entwicklungsumgebung: VS 2010 Professional Herkunft: Freiburg
 |
|
| Zitat von Lumbra: |
| Leider sind die Dateien im Startposting nicht mehr verfügbar. Gibt es eine neue URL? |
Der Inhalt war - soweit ich weiß - identisch mit dem Text im ersten Posting.
|
|
17.07.2009 19:40 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
herbivore
myCSharp.de-Team (Admin)

Dabei seit: 11.01.2005
Beiträge: 42.184
Entwicklungsumgebung: csc/nmake (nothing is faster) Herkunft: Berlin
 |
|
Hallo Lumbra, hallo winSharp93,
| Zitat: |
| Der Inhalt war - soweit ich weiß - identisch mit dem Text im ersten Posting. |
so ist es. Da der Inhalt also weiterhin vollständig enthalten ist, nur eben nicht mehr in unterschiedlichen Dateiformaten vorliegt, habe ich die jetzt unnütz gewordenen Links entfernt.
herbivore
|
|
18.07.2009 00:08 |
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
|