Klasse: "Pass by Reference" falsch?


  • Administrator

    Ich kann mich nur wiederholen. Unterscheidet zwischen Sprach- und Runtime-Level. Auf dem Sprach-Level ist es ein Call-By-Reference und auf dem Runtime-Level ein Call-By-Value. Was bitte schön spricht dagegen?

    Ein Call-By-Value heisst, dass das Objekt kopiert wird. Das ist doch einfach nur verwirrend in C#!

    class MyObject
    {
    };
    
    // ...
    
    void Foo(MyObject obj) { }
    
    // ...
    MyObject obj = new MyObject();
    Foo(obj);
    

    Hier wird doch niemand vernünftigerweise davon reden, dass an Foo eine Kopie von obj übergeben wird? Es ist zwar technisch korrekt, aber damit wird etwas nach vorne geholt, welches von der Sprache transparent gekapselt wird, dass überhaupt keinen Vorteil im Verständnis bringt. Wieso also diese unsinnige Bezeichnung verwenden, welche überhaupt keinen Vorteil bringt?

    Es ist für jeden deutlich verständlicher, wenn man davon spricht, dass eine Referenz auf das Objekt hier übergeben wird und wir somit ein Call-By-Reference haben. Völlig egal und losgeläst, was die Runtime nun macht.

    Theoretisch dürfte diese ja auch das Objekt kopieren, auf der Kopie arbeiten und den Speicher wieder 1:1 auf das ursprüngliche Objekt zurückmappen, wenn es denn in der CLR nicht anders standardisiert wäre.

    Ich plädiere nur dafür, dass man diese beiden Level voneinander klar trennen sollte.

    Grüssli



  • Sorry, ich bin normalerweise immer dabei, wenn man mal fünfe gerade sein lassen soll, aber ich halte es für wichtig, dass man hier sieht, versteht und sagt, dass es im Endeffekt call by value ist.
    Wenn in C# void foo (Object o) von euch als call by reference bezeichnet wird, ist dann void foo(ref Object x) etwa "mehr" call by reference? 😉 Wenn ich Anfänger wäre, würde ich mich fragen: Hääää?

    Man kann es für Wortklauberei halten, ist es aber nicht. Es ist auch nicht schwer zu verstehen, insofern sollte es nicht schwer sein, es korrekt zu benennen. Es stiefelt ja auch keiner durch die Gegend und sagt int x; wäre die Definition und nicht die Deklaration von x.



  • Dravere schrieb:

    Ich kann mich nur wiederholen. Unterscheidet zwischen Sprach- und Runtime-Level. Auf dem Sprach-Level ist es ein Call-By-Reference und auf dem Runtime-Level ein Call-By-Value. Was bitte schön spricht dagegen?

    Also ich rede über die Semantik, was du wohl Sprachebene nennst, nicht über die Umsetzung.

    Ein Call-By-Value heisst, dass das Objekt kopiert wird.

    Nein, es heißt, dass das Argument kopiert wird. Das Argument ist in dem Fall eine Referenz.

    Hier wird doch niemand vernünftigerweise davon reden, dass an Foo eine Kopie von obj übergeben wird?

    Ja, weil das überflüssig ist (und daher ungewohnt klingt). Bei Bar(42) sagt man ja auch nicht, dass eine Kopie von 42 übergeben wird, auch wenn es eigentlich stimmt.

    Es ist völlig richtig, dass eine Referenz auf ein Objekt übergeben wird, nur führt es in die Irre, das Call-by-reference zu nennen. Wie ich schon sagte, man muss irgendwann intellektuell den Schritt machen zu akzeptieren, dass Variablen von Klassentypen (und Arrays) Referenzen sind. Das ist kein Aspekt der technische Umsetzung, das ist IMO essentiell zum Verständnis.

    Nochmal ein Beispiel:

    Derived obj = new Derived();
    Base b = obj;
    

    Wie willst du sowas verstehen, wenn für dich obj das Objekt ist? Was ist dann b? Eigentlich auch obj ... aber hat obj jetzt einen anderen Typ? Nope, das Objekt "schwebt" im Hintergrund, und die beiden Variablen sind unterschiedliche Sichten darauf. Oder halt Referenzen.

    Es ist für jeden deutlich verständlicher, wenn man davon spricht, dass eine Referenz auf das Objekt hier übergeben wird

    Korrekt.

    und wir somit ein Call-By-Reference haben.

    Das folgt daraus nicht. Und es ist IMO irreführend. Schon allein, weil du dann nicht weißt, wie du die Übergabe mithilfe von ref nennen sollst. Aber auch ohne das, die gleiche Diskussion gibts schließlich auch in Java.



  • GPC schrieb:

    Es stiefelt ja auch keiner durch die Gegend und sagt int x; wäre die Definition und nicht die Deklaration von x.

    Hä?



  • volkard schrieb:

    GPC schrieb:

    Es stiefelt ja auch keiner durch die Gegend und sagt int x; wäre die Definition und nicht die Deklaration von x.

    Hä?

    Zumal das hier im Forum jemand macht.



  • GPC schrieb:

    Es stiefelt ja auch keiner durch die Gegend und sagt int x; wäre die Definition und nicht die Deklaration von x.

    "int x;" ist sowohl Deklaration als auch eine Definition. Deklarieren darfst du sooft zu willst, wenn du zweimal "int x;" im selben Block schreibst beschwert sich der Compiler. In C# ist Deklaration aber praktisch immer auch gleich Definition und andersrum.
    Aber das gehört nicht hierher.

    Bashar schrieb:

    Nochmal ein Beispiel:

    Derived obj = new Derived();
    Base b = obj;
    

    Wie willst du sowas verstehen, wenn für dich obj das Objekt ist? Was ist dann b? Eigentlich auch obj ... aber hat obj jetzt einen anderen Typ? Nope, das Objekt "schwebt" im Hintergrund, und die beiden Variablen sind unterschiedliche Sichten darauf. Oder halt Referenzen.

    Es ist für jeden deutlich verständlicher, wenn man davon spricht, dass eine Referenz auf das Objekt hier übergeben wird

    Korrekt.

    und wir somit ein Call-By-Reference haben.

    Das folgt daraus nicht. Und es ist IMO irreführend. Schon allein, weil du dann nicht weißt, wie du die Übergabe mithilfe von ref nennen sollst. Aber auch ohne das, die gleiche Diskussion gibts schließlich auch in Java.

    Und ich finde es verwirrend es nicht "by reference" zu nennen wenn doch eine Referenz übergeben wird. Das dann als "by value" zu bezeichnen passt nicht.

    Die Übergabe mittels ref ist dann die Übergabe der Referenz by Referenze, was denn sonst ?



  • DarkShadow44 schrieb:

    Die Übergabe mittels ref ist dann die Übergabe der Referenz by Referenze, was denn sonst ?

    Du widersprichst dir damit selbst, denn nach der Logik müsste die Übergabe ohne ref "Übergabe der Referenz by Value" heißen.



  • DarkShadow44 schrieb:

    Deklarieren darfst du sooft zu willst, wenn du zweimal "int x;" im selben Block schreibst beschwert sich der Compiler.

    Du meinst sicher, dass ich so oft definieren darf, wie ich will. Vielleicht liest du noch mal genau nach, was der Unterschied zwischen Deklaration und Definition ist 😉

    DarkShadow44 schrieb:

    Und ich finde es verwirrend es nicht "by reference" zu nennen wenn doch eine Referenz übergeben wird. Das dann als "by value" zu bezeichnen passt nicht.

    Die Übergabe mittels ref ist dann die Übergabe der Referenz by Referenze, was denn sonst ?

    Ich finde, diese beiden Aussagen schließen sich gegenseitig aus.



  • GPC schrieb:

    Du meinst sicher, dass ich so oft definieren darf, wie ich will. Vielleicht liest du noch mal genau nach, was der Unterschied zwischen Deklaration und Definition ist 😉

    <a href= schrieb:

    http://www.c-plusplus.net/forum/61231-full pumuckl">

    class foo;          // <-- Deklaration von foo 
      
    class foo {         // <-- Definition  von foo 
     /*...*/
    }; 
        
    extern int yay;     // <-- Deklaration von yay 
    int yay;            // <-- Definition  von yay
    

    Wo liegt jetzt mein Fehler ? 😕

    Bashar schrieb:

    DarkShadow44 schrieb:

    Die Übergabe mittels ref ist dann die Übergabe der Referenz by Referenze, was denn sonst ?

    Du widersprichst dir damit selbst, denn nach der Logik müsste die Übergabe ohne ref "Übergabe der Referenz by Value" heißen.

    Naja letztlich wird bei einer Übergabe immer etwas kopiert, und sei es nur die Referenz. Ja, insofern müsste es ohne ref "Übergabe der Referenz by Value" und mit ref "Übergabe der Referenz by Referenze by Value" heißen. Da würde ich jeweils das letzte "by value" allerdings weglassen, da klar ist dass die Referenz kopiert werden muss, die Methode muss schließlich irgendwas erhalten.



  • Ich hab irgendwie das Gefühl, du hast gar keine Meinung zu dem Thema und reimst dir von Post zu Post irgendwas zusammen. Auch gut, du wirst wohl irgendwann bei der etablierten Bezeichnung ankommen 😉



  • Bashar schrieb:

    Ich hab irgendwie das Gefühl, du hast gar keine Meinung zu dem Thema und reimst dir von Post zu Post irgendwas zusammen. Auch gut, du wirst wohl irgendwann bei der etablierten Bezeichnung ankommen 😉

    Und ich glaube du versuchst es nicht zu verstehen.
    Nochmal als einfachere Fassung:
    By value heißt dass das objekt kopiert wird. By Reference heißt dass es nicht kopiert wird. Ergo:

    void method(Class1 obj) // by reference (auch wenn die referenz auf runtime level sicher by value übergeben wird)
    


  • DarkShadow44 schrieb:

    Und ich glaube du versuchst es nicht zu verstehen.
    Nochmal als einfachere Fassung:

    Einfacher heißt in dem Fall, dass du den größeren Blödsinn ("reference by value") weglässt? Dazu hätte ich ja auch noch was zu sagen gehabt 😉

    By value heißt dass das objekt kopiert wird.

    Nein, das Argument wird kopiert.

    By Reference heißt dass es nicht kopiert wird.

    Nein. Es heißt, dass das Argument als (implizite) Referenz übergeben wird.

    Ergo:

    void method(Class1 obj) // by reference (auch wenn die referenz auf runtime level sicher by value übergeben wird)
    

    Es geht nicht um die Runtime, sondern um die Semantik.



  • Dravere schrieb:

    Ich kann mich nur wiederholen. Unterscheidet zwischen Sprach- und Runtime-Level.
    (...)

    Ich glaub es geht wirklich darum was Dravere schreibt.

    Also uns wurde früher in C++ eben so beigebracht, dass:

    int *i = new int();
    foo(*i) //by value
    foo(i) //by referenz
    

    Dabei ist mir sehr wohl bewusst, dass der Parameter by value übergeben wird.
    Folgendes funktioniert also natürlich nicht:

    void foo(int *i) 
    {i = nullptr;}
    


  • Dazu hab ich doch auch schon was gesagt. Die Übergabe eines Pointers kann Call-by-reference simulieren, aber es ist weiterhin Call-by-value.

    Bei C++-Referenzen kann man sich streiten -- IMHO gehört zum Konzept von Call-by-reference, dass sich das nur auf Parameter bezieht, C++ hat Referenzen jedoch als normalen Datentyp. Nun sind C++-Referenzen keine Objekttypen, können also nicht kopiert werden. Kann man also davon sprechen, dass da eine Referenz by-value übergeben wird, eher nicht. Wird das Argument also by-reference übergeben? Eigentlich schon, aber diese Art der Argumentübergabe unterscheidet sich genaugenommen in nichts von jeder anderen Argumentübergabe, da dort i.A. nicht kopiert wird, sondern Formalparameter mit Aktualarametern in dergleichen Weise wie sonst auch initialisiert werden. Daher tendiere ich zu der Meinung, dass das Konzept von Call-by-reference im Grunde genommen überholt ist und insbesondere in C++ sinnlos geworden ist. Bei Sprachen wie C# passt es jedoch noch. Das ist ja dasselbe wie z.B. in Pascal mit seinen var-Parametern.
    </imho>



  • Ich kann die Argumentation ebenfalls nicht nachvollziehen, dass eine einfache Parameterübergabe in C# Call-by-Reference ist. Es ist Call-by-Value und nichts anderes. Wenn man das verschleiert ist weder angeblich verwirrten Anfängern geholfen, noch kann ich im Gespräch den Unterschied zu echtem Call-By-Reference mittels ref-Modifier ausdrücken.

    Es ist gerade für Anfänger wichtig das irgendwann zu begreifen. Man sieht hier im C# Forum oft wilde Konstrukte aus denen ganz klar hervorgeht, dass der Schreiber nicht verstanden hat, wie es um die Lebenszeit von Objekten, Referenz- und Wertetypen gestellt ist. Bashar hat es schon richtig gesagt, da muss man irgendwann durch und es ist nun wirklich nicht so schwierig zu verstehen.



  • DarkShadow44 schrieb:

    Wo liegt jetzt mein Fehler ? 😕

    In dem verlinkten Beispiel ist alles korrekt. Was ich meinte:

    public void foo() {
      int x; //<- Deklaration
      x = 5; //<- Definition
      x = 4; //<- Erneute Definition, klappt logischerweise
      int x; //<- Zweite Deklaration, würde nicht klappen - ist klar
    
      int y = 10;  //<- Deklaration + Definition in einem
    }
    

    Ich denke, damit können wir das Thema auch ruhen lassen 😉 Wahrscheinlich haben wir in dem Punkt nur aneinander vorbeigequatscht, weil du eher Klassen-/Funktionsdeklarationen im Sinn hattest.

    By value heißt dass das objekt kopiert wird.

    Ich würde dir hier auch dringend empfehlen, mal von "das Objekt"-Sichtweise wegzukommen und einfach festzustellen.. es wird ein Argument übergeben. Das Argument ist nicht das Objekt selbst, wenn es sich um Referenztypen handelt. Vielleicht fällt es dann leichter, dass offensichtliche und faktisch korrekte (= call by value) zu akzeptieren 😉



  • Das Argument ist nicht das Objekt selbst, wenn es sich um Referenztypen handelt. Vielleicht fällt es dann leichter, dass offensichtliche und faktisch korrekte (= call by value) zu akzeptieren

    Und genau da sind wir wieder bei der Unterscheidund zwischen Sprach- und Runtime-Level 😃
    Faktisch ist es korrekt dass das Argument by-value übergeben wird, ja.

    Aber:
    Wenn ich einer Methode ein Objekt übergebe, übergebe ich (semantisch) das Objekt einer Klasse, und nicht ein Handle!
    Insofern ist für mich das Objekt das Argument das übergeben wird, und nach dieser Sichtweise ist meine Übergabe definitv "by reference".

    Darum auch der Vorschlag der Unterscheidung in Runtime - und Sprachlevel.
    Auf Runtimelevel hast du recht, da wird nur ein Handle by-value übergeben.
    Auf Sprachlevel übergebe ich allerdings ein Objekt einer Klasse, daher ist die Übergabe by reference.

    Es ist gerade für Anfänger wichtig das irgendwann zu begreifen. Man sieht hier im C# Forum oft wilde Konstrukte aus denen ganz klar hervorgeht, dass der Schreiber nicht verstanden hat, wie es um die Lebenszeit von Objekten, Referenz- und Wertetypen gestellt ist. Bashar hat es schon richtig gesagt, da muss man irgendwann durch und es ist nun wirklich nicht so schwierig zu verstehen.

    Ich würde jetzt nicht sagen dass das was mit Anfängern oder fehlendem Verständnis zu tun hat. Wir verstehen ja hier alle wie die Übergabe in C# läuft, wir hängen uns hier nur an Definitionen auf. 😃
    Und wenn ich eine struct übergebe und das als "by value" bezeichne, werde ich die Übergabe einer class nicht auch als "by value" bezeichnen, weil es nunmal was komplett anderes macht. Auch wenn im Hintergrund nur ein Handle by-value übergeben wird bezeichne ich die Übergabe als Übergabe einer Referenz.



  • DarkShadow44 schrieb:

    Wir verstehen ja hier alle wie die Übergabe in C# läuft, wir hängen uns hier nur an Definitionen auf. 😃

    Und Microsoft definiert, wie es in C# genannt wird.



  • DarkShadow44 schrieb:

    Auf Runtimelevel hast du recht, da wird nur ein Handle by-value übergeben.
    Auf Sprachlevel übergebe ich allerdings ein Objekt einer Klasse, daher ist die Übergabe by reference.

    Was du Runtime nennst, ist in Wirklichkeit das Sprachlevel. Und was du semantisch nennst, ist deine subjektive Vorstellung.


  • Administrator

    GPC schrieb:

    Ich würde dir hier auch dringend empfehlen, mal von "das Objekt"-Sichtweise wegzukommen und einfach festzustellen.. es wird ein Argument übergeben. Das Argument ist nicht das Objekt selbst, wenn es sich um Referenztypen handelt. Vielleicht fällt es dann leichter, dass offensichtliche und faktisch korrekte (= call by value) zu akzeptieren 😉

    Nein. Mir zumindest nicht. Weil dieses "andere" als das "Objekt" in C# völlig transparent ist und daher eigentlich nicht vorhanden. Ihr führt einfach eine unnötige zusätzliche Definition ein, wodurch es einfach nur komplizierter wird zu verstehen und einem überhaupt nichts bringt.

    Ich finde es so schön, auf der deutschen Wikipedia steht das folgende zum "Spezialfall" Java:
    http://de.wikipedia.org/wiki/Referenzparameter

    Wikipedia DE schrieb:

    In der Sprache Java wird bei primitiven Datentypen automatisch ein Wertparameter verwendet. Bei Objekten wird die Objektreferenz als Wertparameter durch Kopie übergeben. Die aufgerufene Methode kann also den Zustand des referenzierten Objekts verändern, jedoch nicht die ursprüngliche Referenz selbst – sie zeigt also nach der Rückkehr aus dem Methodenaufruf auf dasselbe Objekt wie zuvor. Die Terminologie vieler Java-Publikationen verdeckt diesen Umstand. [1]

    1. Möchte ich hier mal hervorheben, wie interessanterweise sehr viele Java-Publikationen diesen Umstand verdecken. Was mir bisher bei C# ebenfalls aufgefallen ist. Man mag sich hier mal fragen wieso? Aber wahrscheinlich sind das alles Idioten, welche nicht verstehen wie Java/C# funktioniert?
    2. Wenn man dieser Referenz folgt, dann kommt man hierher:
    http://javadude.com/articles/passbyvalue.htm
    Daraus möchte ich das folgende zitieren:

    I'm a compiler guy at heart. The terms "pass-by-value" semantics and "pass-by-reference" semantics have very precise definitions, and they're often horribly abused when folks talk about Java. I want to correct that...

    Und genau da sehe ich das Problem. Der Typ sieht das Ganze aus der Kompilerwelt. Interessanterweise hat er anscheinend auch einen C oder C++ Hintergrund, wie wohl viele von euch auch. Wieso darf es nicht unterschiedliche Definitionen auf unterschiedlichen Ebenen geben? Ja, im Bereich der Runtime/Kompiler ist es ein Call-By-Value aber auf dem abstrakten Sprach-Level ein Call-By-Referenz.

    Ich habe Mühe hier weiter zu argumentieren, da es mir vorkommt, als ob ich jemandem erklären müsste, wieso 2 + 2 gleich 4 ist. Es ist mir völlig rätselhaft, wieso man hier sich so dermassen stur stellen will und unbedingt an irgendwelchen Definitionen festhalten möchte, auch wenn sie überhaupt keinen Mehrwert geben. Das ist Erbsenzählerei, welche niemandem was bringt.

    Könnte mir jemand sagen, wieso es relevant ist, diese Abstrahierung einem C# Programmierer sichtbar zu machen? Vielleicht können wir darüber einen Weg finden? Wieso muss ein C# Programmierer wissen, dass hier tatsächlich das Argument kopiert wird? Ich sehe einfach nicht, was die Vorteile sind, diese Abstraktion wieder sichtbar zu machen. Und bitte keine Argumente à la: "Weil es halt korrekt ist!"

    µ schrieb:

    Es ist gerade für Anfänger wichtig das irgendwann zu begreifen. Man sieht hier im C# Forum oft wilde Konstrukte aus denen ganz klar hervorgeht, dass der Schreiber nicht verstanden hat, wie es um die Lebenszeit von Objekten, Referenz- und Wertetypen gestellt ist. Bashar hat es schon richtig gesagt, da muss man irgendwann durch und es ist nun wirklich nicht so schwierig zu verstehen.

    Das hat doch überhaupt nichts damit zu tun! Im Gegenteil, du verwirrst ihn eben eher noch mehr, wenn du diese seltsame Definition durchziehst. Du kannst es ihm deutlich einfacher erklären, wenn du nicht alles als Call-By-Value definierst.

    Oder vielleicht noch ein anderer Ansatz:
    Vielleicht ist allgemein der Begriff Call-By-Value und Call-By-Reference nicht eindeutig genug? Ich meine eine solche Diskussion wie hier gibt es nicht zum ersten Mal. Man gehe nur in entsprechende Foren suchen. Die Diskussionen wurden schon sehr sehr oft geführt und das zeigt doch, dass die Definitionen einfach schlecht sind, sonst müsste man nicht so oft darüber diskutieren.

    Grüssli


Anmelden zum Antworten