Klasse: "Pass by Reference" falsch?



  • Hi,

    ich bin heute über folgendes gestolpert:

    MYTH #3: “OBJECTS ARE PASSED BY REFERENCE IN C# BY DEFAULT”
    
    This is probably the most widely propagated myth. Again, the people who make this
    claim often (though not always) know how C# actually behaves, but they don’t know
    what “pass by reference” really means.
    (...)
    

    Ist das nur Namenklugscheißerei oder kann man das tatsächlich nicht pass by
    reference nennen in C#?

    Wo wäre der Unterschied zu C++?

    Grüße
    Huddi



  • huddi schrieb:

    Hi,

    ich bin heute über folgendes gestolpert:

    MYTH #3: “OBJECTS ARE PASSED BY REFERENCE IN C# BY DEFAULT”
    
    This is probably the most widely propagated myth. Again, the people who make this
    claim often (though not always) know how C# actually behaves, but they don’t know
    what “pass by reference” really means.
    (...)
    

    Ist das nur Namenklugscheißerei oder kann man das tatsächlich nicht pass by
    reference nennen in C#?

    Wo wäre der Unterschied zu C++?

    Grüße
    Huddi

    In C# werden standardmäßig sowohl Werttypen wie z.B. ein int als auch Referenztypen wie eine Klasseninstanzby by value übergeben. Bei Werttypen wird der Wert an sich kopiert, bei Referenztypen eben die Referenz (nicht das Objekt, das tatsächlich referenziert wird). Erst mit dem ref-Modifier übergibt man by reference.

    Auch in C++ wird standardmäßig alles by value übergeben. Wenn du einen Pointer übergibst, wird der Pointer kopiert. Nur das Objekt, auf das der Pointer zeigt, wird nicht kopiert. Und genau das gleiche ist auch in C# mit Referenzen der Fall 😉



  • Das ist pure Wortklauberei.

    Ich denke das liegt an den Unterscheiden zwischen C++ und C#.
    Folgender C++ Code kopiert das Object beim Aufruf:

    void method(Class1 onj)
    

    Folgender C# Code nicht:

    void method(Class1 onj)
    

    Um das selbe in C++ zu erreichen muss man

    void method(Class1& onj)
    

    oder

    void method(Class1* onj)
    

    schreiben.
    Diesen C++ Code würde man wohl als Pass-by-reference bezeichnen. Der C# Code macht das selbe, soll aber nicht Pass-By-Reference sein ? 🙄

    Und rein logisch ist es auch egal ob man jetzt sagt "Die Adresse des Objekts wird by value übergeben" oder "Das Objekt wird by reference übergeben". Und genau das passiert in C# nunmal wenn man ein Objekt übergibt.

    Wenn man nun hergeht und sagt nur

    void method(ref Class1 obj)
    

    zählt als "by reference", dann darf in C++ auch nur

    void method(Class1** onj)
    

    als "by reference" bezeichnen!



  • huddi schrieb:

    Ist das nur Namenklugscheißerei oder kann man das tatsächlich nicht pass by reference nennen in C#?

    Eigentlich Namenklugscheißerei.

    Die können in ihrer Sprache ihre eigenen Wörter definieren.
    http://msdn.microsoft.com/en-us/library/s6938f28
    und tatsächlich, by default werden Objekte by Value übergeben, wobei die Objekte halt automatisch Referenzen sind. 🤡 😕

    huddi schrieb:

    Wo wäre der Unterschied zu C++?

    In C++ schreibt man hin, was man haben will, kein vereinfachender Automatismus.



  • DarkShadow44 schrieb:

    Das ist pure Wortklauberei.

    Nicht ganz. C# unterscheidet scharf zwischen normalen Parametern und ref- bzw. out-Parametern. Es ist absolut sinnvoll, ersteres als Call-By-Value und letzteres als Call-By-Reference zu bezeichnen.

    Ich denke das liegt an den Unterscheiden zwischen C++ und C#.
    Folgender C++ Code kopiert das Object beim Aufruf:

    void method(Class1 onj)
    

    Folgender C# Code nicht:

    void method(Class1 onj)
    

    Um das selbe in C++ zu erreichen muss man

    void method(Class1& onj)
    

    oder

    void method(Class1* onj)
    

    schreiben.
    Diesen C++ Code würde man wohl als Pass-by-reference bezeichnen. Der C# Code macht das selbe, soll aber nicht Pass-By-Reference sein ? 🙄

    Der C++-Code simuliert höchstens Call-by-reference. Die Bezeichnungen Call-by-value, Call-by-name, Call-by-reference etc. stammen aus Sprachen, in denen diese Konzepte eindeutig umgesetzt sind. Man kann sich lang und breit darüber streiten, wie das in C++ ist, das ganze ist durch Referenzen und verschiedene beim Aufruf mögliche Typumwandlungen beliebig kompliziert, da wird man nie auf einen grünen Zweig kommen. Aber man kann daraus nicht zurückschließen, wie es in einfacher gestrickten Sprachen heißen sollte.



  • In C++ schreibt man hin, was man haben will, kein vereinfachender Automatismus.

    In C# schreibt man auch hin was man will, es läuft nur anders als in C#. 🤡
    Der einzige unterschied ist, dass in C# alle Objekte von Klassen (nur Klassen, nicht Structs) eigentlich Pointer sind. und die verhalten sich genau so wie C++ Pointer.

    Es ist absolut sinnvoll, ersteres als Call-By-Value und letzteres als Call-By-Reference zu bezeichnen.

    Finde ich nicht. Folgender Code:

    void method1(Struct1 obj) // definitiv call by value
    void method1(Class1 obj) // Auch by value ? Eigentlich ja ein Pointer, daher  würde ich das auch als by reference bezeichnen
    
    void method1(ref Struct1 obj) // definitiv call by reference ?
    void method1(Class1 obj) // Auch by reference ? Verhält sich ja eigentlich gleich
    

  • Administrator

    Bashar schrieb:

    DarkShadow44 schrieb:

    Das ist pure Wortklauberei.

    Nicht ganz. C# unterscheidet scharf zwischen normalen Parametern und ref- bzw. out-Parametern. Es ist absolut sinnvoll, ersteres als Call-By-Value und letzteres als Call-By-Reference zu bezeichnen.

    Nein, das verwirrt schlussendlich nur jeden Anfänger. Ich halte das für eine ziemlich dämliche Wortklauberei. Es ist zwar technisch korrekt, aber hilft dem Verständnis rein gar nichts.
    Dass es sich technisch im Hintergrund um eine Kopie eines Zeigers oder dergleichen handelt, ist für den Programmierer völlig irrelevant. Damit wäre quasi ein void foo(Class& obj); in C++ auch kein Call-By-Reference. Es wird schliesslich ein Zeiger per Kopie an die Funktion übergeben. Somit ist es nach dieser Definition ebenfalls ein Call-By-Value. Das ist doch völlig hirnverbrannte Wortklauberei.

    Grüssli



  • DarkShadow44 schrieb:

    In C# schreibt man auch hin was man will, es läuft nur anders als in C#. 🤡

    Ja.



  • DarkShadow44 schrieb:

    Es ist absolut sinnvoll, ersteres als Call-By-Value und letzteres als Call-By-Reference zu bezeichnen.

    Finde ich nicht. Folgender Code:

    void method1(Struct1 obj) // definitiv call by value
    void method1(Class1 obj) // Auch by value ? Eigentlich ja ein Pointer, daher  würde ich das auch als by reference bezeichnen
    
    void method1(ref Struct1 obj) // definitiv call by reference ?
    void method1(Class1 obj) // Auch by reference ? Verhält sich ja eigentlich gleich
    

    War der Rest meines Postings nach dem erste Satz so schwierig zu verstehen?

    Dravere schrieb:

    Nein, das verwirrt schlussendlich nur jeden Anfänger.

    Da müssen sie durch. Auf dem Weg vom Anfänger zum Fortgeschrittenen liegt irgendwann die Einsicht, dass es so ist.

    Ich halte das für eine ziemlich dämliche Wortklauberei. Es ist zwar technisch korrekt, aber hilft dem Verständnis rein gar nichts.
    Dass es sich technisch im Hintergrund um eine Kopie eines Zeigers oder dergleichen handelt, ist für den Programmierer völlig irrelevant.

    Es ist nicht im Hintergrund, und es ist nicht irrelevant. Es ist z.B. unmöglich, Polymorphie einzusetzen, wenn man nicht kapiert, dass eine Objektvariable eine Referenz ist.

    Damit wäre quasi ein void foo(Class& obj); in C++ auch kein Call-By-Reference.

    Auf dieses Argument hab ich schon geantwortet. Ist der Rest meines Postings nach dem ersten Satz zu schwierig zu verstehen?


  • Administrator

    Bashar schrieb:

    Dravere schrieb:

    Nein, das verwirrt schlussendlich nur jeden Anfänger.

    Da müssen sie durch. Auf dem Weg vom Anfänger zum Fortgeschrittenen liegt irgendwann die Einsicht, dass es so ist.

    Gut, man kann sogar das Anfänger wegnehmen. Das verwirrt jeden. Es macht überhaupt keinen Sinn, dies als Call-By-Value zu bezeichnen. Es ist schlussendlich genau das Gleiche wie in C++ mit dem Referenz-Operator einfach nur implizit.

    Wenn du in einem Gespräch über Code dies als Call-By-Value bezeichnest, wird dich jeder nur seltsam anschauen und dich nicht verstehen, da es völlig irrelevant ist, wie das der Kompiler oder Runtime dann tatsächlich umsetzt. Das gilt für C++ wie auch für C# und daher ist das auf dem Sprach-Level ein Call-By-Reference. Ob dass dann in der Ausführung als Call-By-Value umgesetzt wird, ist völlig schnuppe.

    Bashar schrieb:

    Es ist z.B. unmöglich, Polymorphie einzusetzen, wenn man nicht kapiert, dass eine Objektvariable eine Referenz ist.

    Ist auf dem Sprach-Level völlig irrelevant, wie das umgesetzt wird.

    Meiner Meinung nach muss man hier einfach zwei Dinge klar auseinander halten. Es gibt das Sprach-Level und das Runtime-Level. Und es dürfen für diese beiden Levels unterschiedliche Bezeichnungen verwendet werden. Die Umsetzung im Runtime-Level ist allerdings völlig irrelevant für das Sprach-Level.

    Bashar schrieb:

    Ist der Rest meines Postings nach dem ersten Satz zu schwierig zu verstehen?

    Nein, habe es aus irgendeinem Grund nur nicht gesehen.

    Grüssli



  • Dravere schrieb:

    Bashar schrieb:

    Dravere schrieb:

    Nein, das verwirrt schlussendlich nur jeden Anfänger.

    Da müssen sie durch. Auf dem Weg vom Anfänger zum Fortgeschrittenen liegt irgendwann die Einsicht, dass es so ist.

    Gut, man kann sogar das Anfänger wegnehmen. Das verwirrt jeden. Es macht überhaupt keinen Sinn, dies als Call-By-Value zu bezeichnen. Es ist schlussendlich genau das Gleiche wie in C++ mit dem Referenz-Operator einfach nur implizit.

    siehe unten

    Wenn du in einem Gespräch über Code dies als Call-By-Value bezeichnest, wird dich jeder nur seltsam anschauen

    Unplausibel, unrealistisch, unbeweisbar. Was soll das?

    und dich nicht verstehen, da es völlig irrelevant ist, wie das der Kompiler oder Runtime dann tatsächlich umsetzt.

    Ist es in der Tat, denn es geht nicht um die Umsetzung.

    Das gilt für C++ wie auch für C# und daher ist das auf dem Sprach-Level ein Call-By-Reference.

    Achso, das ist so, weil es so ist. Verstanden.

    Den Rest lösch ich mal weg. Über die Umsetzung oder irgendwelche Runtime-Ebenen hab ich nie was gesagt.

    Bashar schrieb:

    Ist der Rest meines Postings nach dem ersten Satz zu schwierig zu verstehen?

    Nein, habe es aus irgendeinem Grund nur nicht gesehen.

    Und jetzt immer noch ignoriert. In Kurzform: Ich akzeptiere keine Analogieschlüsse, die von C++ ausgehen.


  • Administrator

    Du weisst es, alle Argumente welche etwas anderes sagen sind nach deiner Vorstellung nicht aktzeptabel und damit erübrigt sich jede Diskussion. Habe ich auch nichts dagegen. Soll sich jeder selber eine Meinung dazu bilden. Ich werde es definitiv nie als Call-By-Value bezeichnen.

    Grüssli



  • Dravere schrieb:

    Du weisst es, alle Argumente welche etwas anderes sagen sind nach deiner Vorstellung nicht aktzeptabel und damit erübrigt sich jede Diskussion.

    Wie kommst du darauf? Ganz schlechter Stil. 👎



  • DarkShadow44 schrieb:

    [...] dass in C# alle Objekte von Klassen (nur Klassen, nicht Structs) eigentlich Pointer sind. und die verhalten sich genau so wie C++ Pointer.

    Nein, es ist ein verbreiteter Mythos das eine Referenz von C# das Gleiche sei wie ein Pointer in C++, aber es ist dennoch falsch.

    Der C++-Pointer zeigt auf eine Adresse im Speicher.

    Die C#-Referenz referenziert ein Objekt dessen Adresse erst durch die Speicherverwaltung aufgelöst wird. Die Referenz selber ist aber keine Speicheradresse und enthält auch keine Speicheradresse.

    Dravere schrieb:

    Es ist schlussendlich genau das Gleiche wie in C++ mit dem Referenz-Operator einfach nur implizit.

    eben genau das ist es nicht. Die Referenz aus C++ und die Referenz von C# sind nicht das Gleiche. Siehe oben.



  • Nein, es ist ein verbreiteter Mythos das eine Referenz von C# das Gleiche sei wie ein Pointer in C++, aber es ist dennoch falsch.

    Stimmt schon, hinter den Kulissen sieht es bei C# ganz anders aus. Aber das handelt die CLR vollkommen transparent, letztlich verhalten sich "C#-Referenzen" genau so als ob sie Pointer wären. OK, nicht überall, aber zumindest was die Parameterübergabe angeht. Ob eine Methode das Objekt direkt über die Addresse oder über ein Handle und den Umweg über die CLR referenziert ändert für den normalen Programmierer nichts. In beiden Fällen hat man irgendwas was nur auf das Objekt zeigt, sei es nun Handle oder die native Adresse.
    Die Runtime übernimmt die Auflösung der Adressen, deshalb sind "C++ Pointer" und "C# Referenzen" vom Verhalten für den Entwickler durchaus gleich.


  • 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.


Log in to reply