Laden...

Class vs. Struct

Erstellt von DavidT vor 16 Jahren Letzter Beitrag vor 13 Jahren 15.058 Views
DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren
Class vs. Struct

Hallo,

ich progge nun seit 2 Jahren C# aber ich steige hinter Strukturen nicht hinter.
Ich meine, es ist klar das Strukturen auf dem Stack angelegt werden und KLassen auf dem Heap und das dass allokieren von Speicher auf dem Stack wesentlich schneller geht als auf dem Heap, aber irgendwie bin ich noch NIE in die Situation gekommen eine Struktur zu definieren... Im Gegenteil, ich ärger mich immer das Size etc. nur Strukturen sind und ich keine Referenzen habe...

Daher meine Frage: WO und WOZU setzt ihr selbstdefinierte Strukturen ein?

Gruß David

Suchhilfe: Struktur

2.891 Beiträge seit 2004
vor 16 Jahren

Hi,

du wirst irgendwann verhauen, wenn du die Suche nicht benutzt... 😁
Benutze deinen Beitragstitel und du findest Struct vs. Class

Gruß
dN!3L

DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren

Jaja, das hatte ich schon gefunden... Allerdings geht es mir ja nicht drum wo der Unterschied ist (das weiß ich ja), mir gehts drum wo Ihr soetwas praktisch einsetzt, weil das ist es, was ich nicht daran verstehe g

Gruß David

V
86 Beiträge seit 2008
vor 16 Jahren

Ich bin bei dem Thema jetzt auch nicht wirklich schlauer geworden 😄.
Naja, aber was mich interessieren würde, was ist performanter? Ich habe es grade etwas getestet und seh im Primzip keinen Nennenswerten Unterschied, aber mein Programm ist dafür auch nicht sonderlich gut geeignet (erstellt nur 1 class / struct je Datei, sind aber 3000 Dateien, mit Toleranz ergibt sich immer rund 12,5 Sekunden...).

F
10.010 Beiträge seit 2004
vor 16 Jahren

Ist recht einfach.

Structs setzt man ein, bei Iterop, und wenn man ganz sicher ist, das man
kein Referenzverhalten haben möchte, und sich der konsequenzen bewusst ist.

DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren

Aber warum z.B. haben die Framework-Designer bei Microsoft für Point und Size ne Struktur gewählt und keine Class?

4.207 Beiträge seit 2003
vor 16 Jahren

Um ehrlich zu sein - keine Ahnung.

Ich frag mich auch immer, warum die auf die idiotische Idee gekommen sind, DateTime nicht als class zu machen, wovon man einfach ableiten könnte, und gut ist, aber neee 😉

IMHO sollte man um Strukturen einen Bogen machen, der so groß ist wie nur irgends möglich.

PS: Speicher auf dem Heap ist genauso schnell reserviert wie Speicher auf dem Stack - ist in beiden Fällen nur das Verschieben von einem Pointer 😉.

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

630 Beiträge seit 2007
vor 16 Jahren

Hallo,

structs sind effizienter zu speichern.

Der struct-Typ eignet sich für die Darstellung von kompakten Objekten, z. B. Point, Rectangle und Color. Obwohl ein Punkt auch als Klasse dargestellt werden kann, ist eine Struktur in einigen Szenarios besser geeignet. Bei der Deklaration eines Arrays mit 1.000 Point-Objekten reservieren Sie z. B. zusätzlichen Arbeitsspeicher, damit auf jedes Objekt verwiesen werden kann. In diesem Fall wäre eine Struktur weniger speicherintensiv. Verwenden von Strukturen (C#-Programmierhandbuch)

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

1.361 Beiträge seit 2007
vor 16 Jahren

Hallo @ all

tscherno hat vollkommen Recht,
und ich danke den Entwicklern, dass sie DateTime nicht als Klasse implementiert haben.

Was sagt denn die MSDN zur Implementierung von DateTime?

Internally, all DateTime values are represented as the number of ticks (the number of 100-nanosecond intervals) that have elapsed since 12:00:00 midnight, January 1, 0001. The actual DateTime value is independent of the way in which that value appears when displayed in a user interface element or when written to a file.

Was das bringt?

Because the appearance of date and time values is dependent on such factors as culture, international standards, application requirements, and personal preference, the DateTime structure offers a great deal of flexibility in formatting date and time values through the overloads of its ToString method.

(und eben nicht durch seine Speicherung als int year; int month; int day; ...)

Und naja.. intern wird das ganze nun in nem 64-Bit großen Long-Wert abgespeichert.
Dann verbaucht also ein DateTimeObjekt 64 Bit.
Und auf nem 32-Bit System kostet ein Zeiger für ein Verweiseobjekt 32 Bit.
Und angenommen wir arbeiten schon mit nem 64-Bit System (weiß grad nich, ob das C# schon wirklich unterstützt, aber irgendwann sicher) dann is ein Zeiger 64Bit groß.

Also ein Array mit DateTime-Klassen würde doppelt so viel speicher wie ein Array mit Date-Time Strukturen verschlingen.

beste Grüße
zommi

4.207 Beiträge seit 2003
vor 16 Jahren

Ich will jetzt nicht trollen, aber ehrlich gesagt - scheiß drauf, ob das dann halt 32 Bit mehr wären, wofür haben heutige Rechner denn 2 oder 4 GByte RAM?

Und wenn DateTime keine struct wäre, könnte man sie wenigstens für eigene Zwecke ableiten ... aber ne, auch das geht nicht. Und wenn man ein DateTime mal auf "unbekannt" setzen will, kommt man um DateTime? als nullable type ja doch nicht herum - warum dann nicht gleich als Klasse?

Wissensvermittler und Technologieberater
für .NET, Codequalität und agile Methoden

www.goloroden.de
www.des-eisbaeren-blog.de

DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren

Stimme ich dir zu Gollo, das ist mir auch schon Mehrfach aufgefallen...

Aber besonders bei Size und Location fällt es mir immer negativ auf, letztens habe ich sowas wie


panel1.location.X = 25;

versucht und mich gewundert warum es nicht geht (Mir ist klar warum, aber es ist für mich nicht intuitiv). Finde an solchen Stellen Strukturen eher hinderlich als nützlich und was das sparen von Speicher angeht, sehe ich das ebenfalls wie Golo...

Gruß David

630 Beiträge seit 2007
vor 16 Jahren

...wofür haben heutige Rechner denn 2 oder 4 GByte RAM?

Deshalb:

When it comes to the rich and convenient .NET Framework, it's like we're kids in the candy store. "Wow, I don't have to do all that tedious strncpy stuff, I can just '+' strings together! Wow, I can load a megabyte of XML in a couple of lines of code! Whoo-hoo!"

Quelle: Writing Faster Managed Code: Know What Things Cost

Aber im Ernst: Ich finde man sollte versuchen nach bestem Wissen und Gewissen effizienten Code zu schreiben ohne dabei in die Premature-Optimization zu fallen. Was aber bei gesparten 32-Bit aber eindeutig in diese Kategorie fallen würde. Man muss wohl einen Mittelweg finden.

Um zum Topic zurückzukehren: Ich habe in irgendeinem Zertifizierungstrainer von MS-Press gelesen (finde die Stelle gerade nicht), dass man Klassen den Strukturen vorziehen sollte sobald die vom Objekt gehaltene Datenmenge einen bestimmten Richtwert (es waren glaube ich 16 oder 32 kb) überschreitet.

Gruss
tscherno

Edit: Ich selber habe bis jetzt auch praktisch keine Strukturen eingesetzt.

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

V
86 Beiträge seit 2008
vor 16 Jahren

Ich für meinen Fall muss leider auf Effizienz etc. achten, vor allem kann es passieren, dass ich Daten von einigen GB in den Speicher laden muss, daher danke für den Hinweis 😄. Aber macht es überhaupt einen Unterschied wenn man keine long Daten in der Klasse (oder struct) hat? Sondern "nur" Strings?

DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren

Denke nicht, sind ja nur Referenzen auf die Stringobjekte auf dem Heap. Hast dann pro Klasse 32Bit mehr weil du ja die Referenz auf das Objekt auf dem Heap dazu hast.

Gruß David

630 Beiträge seit 2007
vor 16 Jahren

Hallo Virussoul,

warum musst du einige GB in den Speicher laden? 8o
Ich denke dass geht auch anders. In dem du Objekt z.b. Just In Time nachlädst...

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

0
767 Beiträge seit 2005
vor 16 Jahren

Aber besonders bei Size und Location fällt es mir immer negativ auf, letztens habe ich sowas wie

  
panel1.location.X = 25;  
  

versucht und mich gewundert warum es nicht geht (Mir ist klar warum, aber es ist für mich nicht intuitiv).

das liegt aber nicht daran, dass Point ein struct ist, sondern daran, dass Point immutable implementiert ist.

loop:
btst #6,$bfe001
bne.s loop
rts

DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren

Aber besonders bei Size und Location fällt es mir immer negativ auf, letztens habe ich sowas wie

  
panel1.location.X = 25;  
  

versucht und mich gewundert warum es nicht geht (Mir ist klar warum, aber es ist für mich nicht intuitiv).

das liegt aber nicht daran, dass Point ein struct ist, sondern daran, dass Point immutable implementiert ist.

Ok hast recht, habe mich vertan, wollte ein Beispiel mit Settings bringen g Aber ist trotzdem unlogisch in meinen Augen...

3.971 Beiträge seit 2006
vor 16 Jahren

Struct und Klassen haben beide Daseinberechtigungen.

Struct:*Kopieren statt zuweisen, Call by Value *Daten liegen auf Stack (Stapel) *Einfaches und schnelles Speichermanagement (Allokieren und freigeben). Speicher wird automatish reserviert, wenn man die Variable definiert. Freigabe erfolgt sofort, wenn der Scope (aktueller Bereich, Funktion) verlassen wird. Bei Zuweisung wird der alte Wert vor der Zuweisung freigegeben.

Klassen:*Bei Zuweisung wird Verweis kopiert, Call by Referenz *Daten liegen aufm Heap(mehrfach verzweigter Baum) *Manueller Speicher allokierung und Freigabe nötig (bzw. späte Freigabe durch den GC). Bei Zuweisung wird Speicher nicht sofort freigeben.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren

Ok, das mit sofortiger Speicherfreigabe bei Neuzuweisung bzw. Scope-lost wäre mal ein Grund. Aber berücksichtigt Ihr das ernsthaft bei der Entwicklung?

Gruß David

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo 0815Coder,

das liegt aber nicht daran, dass Point ein struct ist, sondern daran, dass Point immutable implementiert ist.

doch liegt daran, dass Point ein struct ist. Point ist nicht immutable. Die Properties X und Y haben Setter.

Hallo DavidT,

Ok hast recht, habe mich vertan, ...

doch, du hast recht und hast dich nicht vertan.

herbivore

V
86 Beiträge seit 2008
vor 16 Jahren

@tscherno
Bild- und Datenverarbeitung von MRT Daten.

Warum nimmt man dann nicht immer wenn man sehr große Daten hat structs? Also die auch sobald sie verarbeitet sind sofort wieder freigegeben werden (ohne es manuell zu lösen).

3.971 Beiträge seit 2006
vor 16 Jahren

Weil Structs standardmäßig Call by Value sind, bei jeder Zuweisung und übergabe an Funktionen, wird der Struct kopiert. Bei größeren Structs hast du dann einen starken Performanceverlust

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

V
86 Beiträge seit 2008
vor 16 Jahren

🙂, wusst ich, meine Frage bezug sich auf eine extreme Datenmenge von structs, d.h. einige 100k oder noch mehr, ob das da Performancevorteile bringt, oder Effizienzvorteile des Arbeitsspeichers, im Prinzip ja schon, wenn die Allokierung sofort greift oder?

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo Virussoul,

aus technischer Sicht sind gerade bei großen "Objekten" Klassen vorzuziehen.

herbivore

1.361 Beiträge seit 2007
vor 16 Jahren

hallo,

ich mag Strukturen 😉

Ich finde (Klassen-)Objekte sind Dinge, die sich ändern können, deren Zustand sich einfach durch Manipulation nach und nach ändern kann.

Strukturen sind einfach Elemente aus einer großen Grundmenge die man herausgreift. (Gut vielleicht stellen sie noch ein paar Methoden zur verfügung, aber sie ändern ihren internen Zustand nicht)

DateTime (ein Datum ändert sicht nicht, es ist ein Zeitpunkt) --> Struktur
Farbe (eine Farbe ändert sich auch nicht) --> Struktur

Klar kann es Sinn machen, Funktionen zu definieren, die eine Farbe auf eine andere abbilden, oder einen Zeitpunkt auf einen andern. Aber dadurch ändert sich doch die Ausgangsfarbe nicht ?!

Ich finde auch, dass es bezogen auf die reale Welt irgendwo Sinn macht.
Nehmen wir uns mal das Fahren eines Autos (zu diskreten Zeitpunkten)

Wenn das Auto fährt ändert sich seine Position (als Punkt abgespeichert).

Ganz klar: Das Auto wird nicht gegen ein anderes Auto ausgetauscht, sondern es verändert seinen eigenen Zustand (die Position), bleibt aber im Prinzip das selbe.
Die aktuelle Position als Punkt im dreidimensionalen... verändert sich aber nicht.
Wie sollte sich auch ein fester Punkt im Raum verändern?!?! Sie wird enfach gegen einen andern ausgetauscht.
Deshalb: Auto -> Klasse, Punkt -> Struktur.

Klar sind die grenzen irgendwo auch schwammig, aber manchmal sprechen Dinge finde ich ganz klar für eine der beiden Prinzipien.

Bei Dateiheadern sind Strukturen übrigens auch sehr elegant. (Da hatte ich sie zu meinen C++ Zeiten jedenfalls am häufigsten verwendet)
Man liest einfach direkt in die Struktur ein, oder schreibt sie direkt.
Eben einfach ein Stück "irgendwas" (Speicher, Datei...) mit Struktur.
Und das ganz ohne Serialisierung

Strukturen würde ich mit den andern Werttypen (int, double, char) insofern seehr eng verknüpfen. Und warum dürfe man nicht nen int als ne Struktur aus 4 bytes auffassen? 😉

DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren

Ich mag es wenn ich recht habe, obwohl ich dachte das ich mich vertan hab, weil ich was anderes meinte wo ich trotz das ich unrecht hatte trotzdem recht hatte... ähhhh !?!?

0
767 Beiträge seit 2005
vor 16 Jahren

Ich mag es wenn ich recht habe, obwohl ich dachte das ich mich vertan hab, weil ich was anderes meinte wo ich trotz das ich unrecht hatte trotzdem recht hatte... ähhhh !?!?

einerseits hab ich mich vertan, Point ist nicht immutable.

andererseits funkioniert das da aber auch super:


Point p = new Point(2, 3);
p.X = 5;

Dass DativTs Beispiel nicht kompiliert führe ich jetzt mal darauf zurück, dass der Compiler merkt, dass der Code eigentlich so aussehen müsste:


Point p = panel1.Location;
p.X = 25;
panel1.Location = p;

loop:
btst #6,$bfe001
bne.s loop
rts

S
8.746 Beiträge seit 2005
vor 16 Jahren

Ok, das mit sofortiger Speicherfreigabe bei Neuzuweisung bzw. Scope-lost wäre mal ein Grund. Aber berücksichtigt Ihr das ernsthaft bei der Entwicklung?

Ist selten, aber kommt vor. Die Klasse Point ist ein gutes Beispiel. Wäre das ein Klasse könnte man den GC und die Anwendung gut ins Schwitzen bringen.

DavidT Themenstarter:in
998 Beiträge seit 2007
vor 16 Jahren

Warum würde die ins schwitzen kommen? Weil z.B. beim verschieben eines Fensters tausende von neuen point-objekten erzeugt würden?

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo DavidT,

nein, ich denke, svenson meint, dass es gerade bei Point gut vorstellbar ist, dass man davon sehr viele Points hat und so der GC (unnötig) viel Arbeit bekommt.

Hallo 0815Coder,

natürlich geht

Point p = new Point(2, 3);
p.X = 5;

super, gerade weil Point nicht immutable ist. Es wäre ja irgendwie blöd, wenn man einen Setter hätte und damit das "Objekt" nicht ändern könnte.

Wenn obj.Prop.Prop2 = ... nicht geht liegt das trotzdem daran, dass obj.Prob einen Strukt liefert. Und in dem Wort "liefern" liegt genau das Problem. Wie ist denn der Getter von obj.Prop zwangsläufig implementiert? So dass der Wert per return zurückgegeben wird. Genauso wie bei einer Methode obj.GetProp ().Prop2 = .... Und so ein Rückgabewert ist bei Werttypen nunmal eine temporäre Kopie. Daher würde ...Pro2 = ... nur diese temporäre Kopie ändern. Das würde zwar prinzipiell gehen, aber da die temporäre Kopie am Ende der Anweisung futsch ist, ohne dass man irgendwie darauf zugreifen kann, wäre das nicht sinnig. Und deshalb verhindert des Compiler das.

Bei Referenztypen dagegen liefert return eine Referenz auf das Objekt und deshalb arbeiten darauf angewendete Operationen auf dem Original-Objekt. Deshalb geht bei Referenztypen obj.Prop.Prop2 = ....

herbivore

N
98 Beiträge seit 2006
vor 16 Jahren

Ich verwende Strukturen gerne wenn ich viele Objekte laden will die sich nicht mehr ändern. Z.B. lade ich alle Normalien eines Herstellers als Strukturen da mein Programm diese nur Anzeigen aber nicht verändern soll. Als ich mich damals im Openbook über das Thema eingelesen hab hieß es das die Sturkuturen bei solchen Fällen(wenig Daten pro Objekt; sehr viele Objekte) weniger Overhead erzeugen wars für mich eigtentlich klar was zu nehmen ist.

Desweiteren verwende ich auch gern mal Strukturen wenn ich öfters sehr viele Werte zwischen Methoden hin- und herschieben muss. Sprich ich übergebe einfach eine Struktur statt zig einzelne Werte. Das mach ich aber weil ich mich dann beim Programmieren leichter tue und weniger weils n tieferen Sinn hat.

0
767 Beiträge seit 2005
vor 16 Jahren

Desweiteren verwende ich auch gern mal Strukturen wenn ich öfters sehr viele Werte zwischen Methoden hin- und herschieben muss. Sprich ich übergebe einfach eine Struktur statt zig einzelne Werte.

Das ist aber ein klarer Fall für Klassen. Wenn du die Struktur übergibtst, wird die Struktur komplett kopiert, also alle einzelnen Werte. Wenn die Struktur sagen wir 1kb Daten enthält wird also 1kb kopiert. Bei einer Klasse würde nur die Referenz kopiert werden (ich würd bei 32bit Maschinen auf 4 Byte tippen, aber das kann durch .net overhead auch etwas mehr sein), also deutlich weniger Performanceverlust.

loop:
btst #6,$bfe001
bne.s loop
rts

S
8.746 Beiträge seit 2005
vor 16 Jahren

Strukturen machen dann Sinn, wenn man sehr viele "Datengruppen" erzeugen möchte, die nur eine kurze Lebensspanne haben, i.d.R. innerhalb einer Funktion. In allen anderen Fällen sind Objekte vorzuziehen. Eine weitere Ausnahme ist das Marshalling zu unmanaged Speicher, wenn eine "Datengruppe" eingebettet, bzw. als eingebettetes Array gemarshallt werden soll.

N
98 Beiträge seit 2006
vor 16 Jahren

Desweiteren verwende ich auch gern mal Strukturen wenn ich öfters sehr viele Werte zwischen Methoden hin- und herschieben muss. Sprich ich übergebe einfach eine Struktur statt zig einzelne Werte.

Das ist aber ein klarer Fall für Klassen. Wenn du die Struktur übergibtst, wird die Struktur komplett kopiert, also alle einzelnen Werte. Wenn die Struktur sagen wir 1kb Daten enthält wird also 1kb kopiert. Bei einer Klasse würde nur die Referenz kopiert werden (ich würd bei 32bit Maschinen auf 4 Byte tippen, aber das kann durch .net overhead auch etwas mehr sein), also deutlich weniger Performanceverlust.

Guter Einwand, werd da wohl nochmal darüber nachdenken.

J
32 Beiträge seit 2008
vor 16 Jahren

Man sollte imho auch in die Auswahl ob Struct oder Class einfließen lassen,
dass die größe des zur Verfügung stehenden Stacks begrenzt ist.

Die gewünschte Stack Größe kann man z.B. beim erzeugen eines neuen Threads mit angeben.

Grundsätzlich denke ich dass man structs am besten für folgende Szenarien verwendet:

  • kleine eigene Datentypen
  • man benötigt keine Vererbung
  • Referenztyp Verhalten ist nicht notwendig
  • Es werden keine rießigen Datenmengen erwartet

bei einer Klasse sieht es dann entsprechend anders aus.

Datenbankentitäten würd ich z.B. als Klasse modelieren,
weil die zu verarbeitende und weiterzugende Datenmenge rießig sein kann.
(muss natürlich nicht sein, wenn man immer nur kleine Teilmengen benötigt)

Beispiel:
Entität Person wird auf den DotNet Typ Person gemappt

  • davon werden z.B. 100 Objekte von diesen Typ erzeugt
    Diese Objekte werden durch eine Methode z.B. MapToList(...) in einer Collection gespeichert und von dieser Methode zurückgeliefert.

Ist Person als struct implementiert, müssen alle 100 Objekte kopiert werden,
bei Klassen muss nur der Verweis zurückgegeben werden.

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

da Eric Lippert das gerade veröffentlicht und es zu diesem Thema passt:
The Truth About Value Types
Wie der Titel schon sagt wir dort die Wahrheit erklärt 😉 Es geht v.a. um die Hintergründe zu Stack/Heap und warum wieso das so ist.

mfG Gü

PS: Mir ist schon klar dass der Beitrag alt ist, aber über die Suche kommt man aktuell hier her 😄

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

1.346 Beiträge seit 2008
vor 13 Jahren

Auh wenn das ganze hier schon was Älter ist möchte ich mal meinen Senf dazugeben. Und zwar finde ich Structs ziemlich praktisch in der Netzwerkkommunikation, wenn es darum geht datenpakete möglichst schnell und ohne viel Traffic durchs Netz zu schicken. zB bei Spielen. Wenn man anfängt eine Klasse zu serialisieren entsteht meist mehr Overhead als eigendlich daten. Da finde ich structs ziemlich praktisch, da man sie ohne overhead übers netz schieben kann.

Gruß pdelvo

5.299 Beiträge seit 2008
vor 13 Jahren

So performancefragen, ob stack oder heap sind eigentlich nur sehr selten von Belang.

Herbivore hat mal den IMO schlauen Gedanken beigesteuert, dass ReferenzTypen eine Identität haben, WerteTypen nicht.

Betrachtet man eine sehr einfache Struct, nämlich int:
Die hat keine Identität - es macht keinen Sinn, zu sagen: "Hier ist eine 5, und das hier ist auch eine 5, aber eine andere"

Creiert man aber eine Klasse, mit einer int-Property, dann ist es durchaus sinnvoll zu sagen:
"Hier sind 2 verschiedene myClass-Instanzen, beide mit .IntValue=5".

Und so fasst MS Points und DateTimes wohl auch als identitäten-los auf - P(5/3) ist eben P(5/3), und es ist sinnlos zu sagen: "dieser P(5/3) ist ein anderer P(5/3) als jener."

Structs musses halt geben, denn irgendwo müssen die Werte ja schließlich stehen im Speicher. Wenn alles nur Verweistypen wären, würde nur von einem Element auf seine Unter-Elemente verwiesen, usw., und ein Wert wäre letztendlich nirgends.

Der frühe Apfel fängt den Wurm.

6.911 Beiträge seit 2009
vor 13 Jahren

Hallo,

So performancefragen, ob stack oder heap sind eigentlich nur sehr selten von Belang.

Das und das andere steht eigentlich alles (fast) genauso im obigen Link 😉

Wenn alles nur Verweistypen wären, würde nur von einem Element auf seine Unter-Elemente verwiesen, usw., und ein Wert wäre letztendlich nirgends.

Kann ich nicht zustimmen, oder ich versteh nicht was du ausdrücken willst.
Der Hauptgrund warum es Werttypen gibt ist dass Copy-by-Value möglich ist, sonst gäbe es nur Copy-by-Reference. Der "Wert" ist in beiden Fällen vorhanden (wie auch immer "Wert" angesehen wird).

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo gfoidl,

Wenn alles nur Verweistypen wären, würde nur von einem Element auf seine Unter-Elemente verwiesen, usw., und ein Wert wäre letztendlich nirgends.

die Aussage ist vollkommen ok. Es geht darum, was für Member ein Typ enthält. Da muss es einen (rekursiven) Abschluss geben. Wenn es nur Verweistypen gäbe, dann wären auch alle Member von Verweistypen selber wieder Verweistypen und es gäbe keine Stelle, an der man die eigentlichen Daten stehen. Es muss also mindestens einen Typ geben, der kein Verweistyp ist oder zumindest direkt Daten enthalten kann.

Wertypen/Structs sind also der (rekursive) Abschluss (quasi) jeder Verweiskette.

Im letzten Satz steht das "quasi" deshalb, weil Verweistypen, die gar keine Member enthalten auch ein (rekursiver) Abschluss wären. Allerdings machen Verweistypen, die gar keine Member enthalten, kaum Sinn. Sie lösen das Problem, dass ja irgendwo die eigentlichen Daten stehen müssen nicht.

herbivore

1.378 Beiträge seit 2006
vor 13 Jahren

Mir war gerade so "langweilig" dass ich folgendes fabriziert hab.

Eine unvollständige eigene "Implementierung" von Int32 ohne Wertetypen. 😛

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        MyInt32 number = 320000000;

        Console.WriteLine(number);

        Console.Read();
    }
}

public class MyInt32
{
    private readonly MyByte[] Bytes = new MyByte[]
        {
            new MyByte(),
            new MyByte(),
            new MyByte(),
            new MyByte()
        };

    public override string ToString()
    {
        return ((int)this).ToString();
    }

    public static implicit operator int(MyInt32 number)
    {
        string[] bits = number.Bytes.SelectMany(b => b).Select(b => b == null ? "0" : "1")
            .Reverse()
            .ToArray();

        string binaryString = string.Join("", bits);

        return Convert.ToInt32(binaryString, 2);
    }

    public static implicit operator MyInt32(Int32 number)
    {
        string binaryString = Convert.ToString(number, 2).PadLeft(32, '0');

        int index = 32;


        MyInt32 result = new MyInt32();

        foreach (var myByte in result.Bytes)
        {
            for (int i = 0; i < 8; i++)
            {
                myByte[i] = binaryString[--index] == '1' ? MyBit.Value : null;
            }
        }

        return result;
    }
}

public class MyBit
{
    public static readonly MyBit Value = new MyBit();
}

public class MyByte : IEnumerable<MyBit>
{
    private readonly MyBit[] Bits = new MyBit[8];

    public MyBit this[int index]
    {
        get { return Bits[index]; }
        set { Bits[index] = value; }
    }

    public IEnumerator<MyBit> GetEnumerator()
    {
        foreach (var bit in Bits)
            yield return bit;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

U
282 Beiträge seit 2008
vor 13 Jahren

Ist pedantisch, aber

b => b == null ? "0" : "1"

An dieser stelle verwendest du einen Pointer als Wert-Typ, weil nicht mehr das, worauf der Pointer zeigt, relevant ist, sondern nur noch, aber er null ist oder nicht.

Letztendlich hättest du auch ein object[] nehmen können und mittels null oder nicht null die Bits codieren.

Dennoch eine Nette Idee 😉

1.361 Beiträge seit 2007
vor 13 Jahren

Hi,

Mir war gerade so "langweilig" dass ich folgendes fabriziert hab.

Mir auch ! 😁

Man kann die objektorientierte (Referenzen nutzendene) Implementierung von natürlichen Zahlen relativ kanonisch aufsetzen: Es gibt die Null als Zahl und es gibt den Nachfolger einer Zahl. Der Rest ergibt sich 😉 (So ist beispielsweise die "5" der 5-fache nachfolger der Null. )

Addition und (saturierte) Subtraktion hab ich auch implementiert. Der Großteil der Logik (aka "Magie") steckt in dem objektorientierten Methoden-Dispatch-Mechanismus, der abhängig vom Typ die richtige Methode aufruft. Für die elegante Implementierung dann noch etwas Double-Dispatching, viel Rekursion und tadaaaa:

public abstract class Number
{
    public static Number operator+(Number x, Number y) { return x.Add(y); }
    public static Number operator-(Number x, Number y) { return y.ReverseSub(x); }

    abstract internal Number Add(Number other); // zwei Zahlen addieren
    abstract internal Number ReverseSub(Number other); // zwei Zahlen umgekehrt subtrahiere, damit das Dispatching klappt
    abstract internal Number SubSuccessor(Successor other); // einen Nachfolger subtrahieren

    public static Number Zero { get { return new ZeroNumber(); } }

    #region Nur zur Kommunikation mit der struct-verwöhnten Außenwelt
    abstract public uint Value { get; }

    public static Number FromValue(uint value)
    {
        return (value == 0) ? Zero : new Successor(FromValue(value - 1));
    }
    #endregion

    internal class ZeroNumber : Number
    {
        internal ZeroNumber() { }
        override internal Number Add(Number other) { return other; } // 0+x=x
        override internal Number ReverseSub(Number other) { return other; } // x-0=x
        override internal Number SubSuccessor(Successor other) { return this; } // 0-x=0 (saturiert)

        public override uint Value { get { return 0; } }
    }

    internal class Successor : Number
    {
        private Number predecessor;

        internal Successor(Number predecessor) { this.predecessor = predecessor; }
        override internal Number Add(Number other) { return this.predecessor.Add(new Successor(other)); } // x+y = (x-1)+(y+1)
        override internal Number ReverseSub(Number other) { return other.SubSuccessor(this); } // DoubleDispatch
        override internal Number SubSuccessor(Successor other) { return other.predecessor.ReverseSub(predecessor); } // x-y = (x-1)-(y-1)

        public override uint Value { get { return predecessor.Value + 1; } }
    }
}

Und verwendet wirds dann halt so:

class Program
{
    static void Main(string[] args)
    {
        Number a = Number.FromValue(9);
        Number b = Number.FromValue(13);
        Number sum = a + b;
        System.Console.WriteLine("{0}+{1}={2}", a.Value, b.Value, sum.Value); // 9 + 13 = 22
        System.Console.WriteLine("{0}-{1}={2}", sum.Value, b.Value, (sum - b).Value); // 22 - 13 = 9
        System.Console.WriteLine("{0}-{1}={2}", sum.Value, a.Value, (sum - a).Value); // 22 - 9 = 13
    }
}

Also mir gefällt das 🙂
Bringt uns zwar nicht weiter, aber man kann schon ganz schön weit gehen mit der struct-losen Welt. (So sind in Smalltalk beispielsweise True und False auch zwei Singleton-Objekte und keine "Werttypen")
beste Grüße
zommi