Laden...

Effekt von Zuweisungen von Wert-, Referenz- und Interfacetypen

Letzter Beitrag vor 15 Jahren 10 Posts 2.048 Views
Effekt von Zuweisungen von Wert-, Referenz- und Interfacetypen

Hallo zusammen
Ich bereite mich gerade auf eine Prüfung in C# vor und leider gibt es in diesem Zusammenhang noch einige Unklarheiten. Ich habe folgenden Beispielcode:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BFH
{
   public class BoxingUnboxing {
      interface ISomething {
         string TheString {
            get;
            set;
         }
      }
      class MyClass : ISomething {
         public string TheString {
            get;
            set;
         }
      }
      struct MyStruct : ISomething {
         public string TheString {
            get;
            set;
         }
      }
      public static void Test() {
         ISomething i;
         MyStruct s = new MyStruct { TheString = "Hello " };
         MyClass c = new MyClass { TheString = "World!" };
         i = s;
         i.TheString += c.TheString;
         Console.WriteLine("i: {0}", i.TheString); => => i: Hello World!
         Console.WriteLine("s: {0}", s.TheString); => s: Hello
         i = c;
         i.TheString = s.TheString + c.TheString;
         Console.WriteLine("i: {0}", i.TheString); => => i: Hello World!
         Console.WriteLine("c: {0}", c.TheString); => c: Hello World!
         ((ISomething)s).TheString = "Ooops!";
         ((ISomething)c).TheString = " Wow!";
         Console.WriteLine("s: {0}", s.TheString); => s: Hello
         Console.WriteLine("c: {0}", c.TheString); => c: Wow!
      }
   }
}


Wieso ist nun die Ausgabe des letzten s.TheString "Hello" und nicht "Ooops!" ?

Console.WriteLine("i: {0}", i.TheString); => => i: Hello World!

was ist denn das => => für eine schreckliche syntax??? ^^

was ist denn das => => für eine schreckliche syntax??? ^^

ich würde sagen, das das nur die consolenausgabe markieren soll.

@Ishildur:

das hier ist ein böses und sehr verwirrendes spiel mit referenz und wertetypen.

das hier ist ein böses und sehr verwirrendes spiel mit referenz und wertetypen.

Hehe, dachte ich mir schon, leider wird die Prüfung mit solchen Sachen vollgespickt sein 😉

Wieso wird denn nun der Wert nicht zugewiesen, wenn ich vorher explizit in ein Interface caste? Das verstehe ich nun echt nicht X(

Hi Ishildur,

Wieso ist nun die Ausgabe des letzten s.TheString "Hello" und nicht "Ooops!" ?

Aus dem selben Grund wie:

Console.WriteLine("i: {0}", i.TheString); => => i: Hello World!
Console.WriteLine("s: {0}", s.TheString); => s: Hello

Weil Wert-Typen (structs) eben keine "Identität" besitzen. Sie werden einfach wild hin und her kopiert. Und dies tritt auch beim Boxing/Unboxing auf. (Also bei der automatisierten Kapselung eines Wert-Objekts in einem Referenz-Objekt)

Wann muss geboxed werden? Wenn ein Wertyp in einer Referenz-Variablen gespeichert werden soll. Und auch deine Interface-Variable "ISomething i" verlangt somit ein Boxing. Also bedeutet das Casten eines Structs in ein Interface immer ein Boxing.

Im obersten Beispiel ist dieser Cast implizit:

i = s;

Im untersten ist er explizit:

((ISomething)s)

In beiden Fällen wird aber ein neues Objekt (Referenz-Typ) erzeugt, dass ein "MyStruct"-Objekt aufnehmen kann. Dieses wird dann auch gefüllt. (Und damit kopiert)

Alle Änderungen an diesem neuen Objekt verändern aber natürlich in keinster Weise das Ausgangsobjekt.

That's all.

beste Grüße
zommi

Noch als Nachtrag: Bei Interfaces muss geboxed werden (genau wie bei einem cast einer Struktur in ein "object") da eine zusätzliche Funktionen-tabelle an das Objekt gebappt werden muss. (Und bei einem Wert-Typ ist kein Platz dafür, während ein ReferenzTyp in der .NET-Runtime eben noch zusätzilche versteckte Felder dafür besitzt)

also prinzipiell ist das beispiel teuflisch, da es einige eigenheiten von string ausnutzt und dann noch die referenz-wertetyp sache.

string ist prinzipiell ein referenztyp. das besondere an ihm ist, das er immutable ist. das bedeutet, das jegliche änderung an einem string, eine neue instanz von sring bilded. daher fühlt er sich wie ein wertetyp an, ist es aber nciht.

beispiel:

variable A wird mit "Hallo" initialisiert und hat addresse 111
somit steht in variable A -> 111

nun wir variable B mit A initialisiert.
somit steht in variable B -> 111

an andresse 111 steht "Hallo"

nun wird gesagt: B = " welt"

es wird nun ein neuer string gebilded.
B zeigt nun auf addresse 222 -> "Hallo welt"
A zeigt auf addresse 111 -> "Hallo"

genau das selbe macht auch das von dir genannte beispiel.

zommi war schneller aber ich poste dennoch 😃

@Jack:
ich widerspreche dir ungern aber:

[...] da es einige eigenheiten von string ausnutzt [...]

trifft leider hier nicht zu 🙁
Das obige Beispiel ist unabhängig davon ob nun ein String, ein nicht immutable Object, ein Werttyp (Point) oder ein eingebauter Typ wie int verwendet wird.
Also diesmal keine String-Zauberei 😁

Liegt alles nur am Boxing/Unboxing von Wert-Typen beim Casten in Interfaces.

beste Grüße
zommi

ein nicht immutable Object

im falle eines nicht immutable object, würde dieser effekt nicht eintreten.

@ Jack.

Dir geht es wahrscheinlich um die Zeile mit dem "+=". Weil das wäre ja der einzige Punkt wo sich ein immutable Objekt von einem variablen Objekt unterscheiden würde.

Aber da ja in C# der "+="-Operator nicht einzeln überladen werden kann, wird das Ausgangsobjekt dort nie verändert werden können. Es wird einfach ersetzt.
Aber dort arbeiten wir ja schon mit einem beboxeten, kopierten Objekt. Also ist es eh egal, was dort genau passiert. Da dies so oder so keinen Einfluss auf das Ausgangsobjekt hat.

Wenn man nun den +-Operator mit hässlichen Seiteneffekten belegt, die vielleicht alle aktuellen Instanzen der Klasse verändern, dann würde sich evtl auch das Ausgangsobjekt verändern bei der += Anweisung. Aber von solch skurrilen Dingen nehme ich mal Abstand.

beste Grüße
zommi

mir geht es eher um diese zeile:

((ISomething)s).TheString = "Ooops!";

aber ich habe mich geirrt 😃 du hast recht das geboxte objekt wird zwar geändert aber das kann in keinem fall das ursprungsobjekt verändern (außer in dem beschriebenen skurielen fall)