Autor Beitrag
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Di 29.01.13 14:46 
Hallo Leute,

ich hab heute das erste mal ein operator-overloading gemacht. Opfer war "==".
Allerdings moserte VS2010 herum, das ich zwar "==" überladen hätte, aber nicht "Equals(object o)" und auch nicht "GetHashCode()". Zum Thema "Equals(object o)" habe ich was gefunden - aber GetHashCode() ?

Wie soll GetHashCode() denn überhaupt aussehen ? Kann mir da so gar keinen Begriff von machen.

Anbei etwas Source, damits nicht so langweilig aussieht:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
    public struct GPSKoord
    {
        public int Grad;
        public int Minute;
        public double Sekunde;
        public char Dir;
        public bool IsValid;

        public static bool operator ==(GPSKoord P1, GPSKoord P2)
        {
            if (ReferenceEquals(P1, null) || ReferenceEquals(P2, null))
                return false;

            return ((P1.IsValid == P2.IsValid) &&
                   (P1.Grad == P2.Grad) &&
                   (P1.Minute == P2.Minute) &&
                   (P1.Sekunde == P2.Sekunde) &&
                   (P1.Dir == P2.Dir));
        }
        public static bool operator !=(GPSKoord P1, GPSKoord P2)
        {
            return (!(P1==P2));
        }

        public override bool Equals(object obj)
        {
            if (!(obj is GPSKoord)) return false;

            return this == (GPSKoord)obj;
        }
    }

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 29.01.13 15:43 
Zitat:
Wie soll GetHashCode() denn überhaupt aussehen ? Kann mir da so gar keinen Begriff von machen.


blogs.msdn.com/b/eri...for-gethashcode.aspx

Es gibt da kein festes Schema wie GetHashCode aussehen soll. Das hängt zu sehr von der Klasse ab die du gerade an der Hand hast. Der Hash sollte sinnvoller Weise nur sehr schnell ermittelbar sein (schneller als Equals seinen vergleich macht) und möglichst konstant sein (wenn sich der Inhalt des Objects ändert sollte sich möglichst der Hashcode nicht ändern).

Bevor man da irgendwas sinnvolles rät wäre es gut zu wissen warum du einen mutable struct programmierst. Das ist eigentlich etwas was man nie haben will.
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Di 29.01.13 15:54 
Ich denke mal, es geht ihm jetzt gar nicht darum, groß GetHeshCode() zu überladen, sondern einfach nur, dass der Compiler nicht mehr meckert.

Wenn du kein Interesse daran hast, GetHashCode() wirklich zu überladen, dann mach einfach folgendes:

ausblenden C#-Quelltext
1:
2:
3:
4:
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }


Damit rufst du die GetHashCode()-Methode von object auf, also passiert nichts anderes, als vorher, wenn GetHashCode() nicht überladen wird.



Dann würde ich allerdings noch den Inhalt der Operator-Überladung von == in die Equals(object obj)-Methode verlagern und dann einfach nur aufrufen. Das empfinde ich als übersichtlicher und als besseren Stil, aber im Endeffekt ist das jedem selber überlassen.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 29.01.13 16:01 
Zitat:
Damit rufst du die GetHashCode()-Methode von object auf, also passiert nichts anderes, als vorher, wenn GetHashCode() nicht überladen wird.


Er hat einen struct. Heißt jede Zuweisung erzeugt ein Kopie des struct. Und die Standard Implementierung würde jedesmal einen neuen anderen Hashcode würfeln. Sein Equals Operator hält aber Kopien für identisch was sein GetHashCode dann aber anders sieht. Das wird mittelfristig zu Problemen führen.
Edit : Ich ziehe die Aussage zurück. ValueType.GetHashCode scheint sich anders zu verhalten.

Erst sollten wir klären warum das ein struct ist.
Palladin007
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1282
Erhaltene Danke: 182

Windows 11 x64 Pro
C# (Visual Studio Preview)
BeitragVerfasst: Di 29.01.13 18:15 
Das ist mir allerdings auch nicht wirklich klar :D


Ich muss aber auch zugeben, wirklich Verwendungszweck für ein struct hab ich bisher noch nicht gefunden.
Ich wollte ja mal einen neuen Zahlentyp entwerfen, der auf einem byte-Array beruht und dessen maximale Größe vom RAM oder im Konstruktor fest gelegt wird, aber da ist ja eigentlich auch eine Klasse sinnvoller.
OlafSt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Mi 30.01.13 11:27 
Also...

Ein struct ist das aus einem simplen Grund: Das ganze ist eine 1:1-Portierung aus Delphi. Da sind diese Dinger allesamt Records. Nach der Portierung kamen, wie wir das alle ja zur genüge kennen, "Sonderwünsche" hinzu, die nun auch einen Vergleich auf "existiert schon in meiner Liste" erforderlich machen.

Ich hielt es nun für eine gute Idee, einfach den struct zu erweitern, anstatt das alles nun auf eine Klasse umbauen zu müssen. @Palladin007 hat das schon ganz richtig erkannt: Eigentlich will ich nur die Warnung loswerden. Ich kann es auf den Tod nicht ausstehen, wenn meine Programme nicht meldungsfrei durchcompiliert werden. Selbst den Hints eines Delphi-Compilers wird unerbittlich nachgegangen...

Wenn ich nun das stuct-Geraffel in Klassen verwandele - kann ich dann auf das operator-overloading verzichten (es wird grundsätzlich nur auf == getestet - zumindest bisher) ? Dann würde sich der Aufwand wieder lohnen.

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 30.01.13 12:31 
Zitat:
Eigentlich will ich nur die Warnung loswerden


Nein. Du willst (oder zumindest solltest) das Problem auf das dich der Compiler hinweist lösen. Einfach loswerden der Warnung heißt nicht zwingend das man danach funktionierenden Code hat.

Die Listenklassen des Frameworks verwenden GetHashcode bevor sie Equals zu raten ziehe. Wenn GetHashcode also fälschlicherweise meldet das 2 struct ungleich sind da sie unterschiedliche Hashes liefern ist es egal wenn equals behaupten würde sie sind identisch. Soweit kämme es erst gar nicht. Du kannst die Warnung erstmal so loswerden wie Paladin007 beschrieben hat. Aber sobald sich dein struct merkwürdig verhält wenn du Hashsets, Dictionaries etc. verwendest solltest du wissen das du an der Stelle noch eine offene Flanke hast die es zu koorigieren gilt.

Zitat:
Wenn ich nun das stuct-Geraffel in Klassen verwandele - kann ich dann auf das operator-overloading verzichten (es wird grundsätzlich nur auf == getestet - zumindest bisher) ? Dann würde sich der Aufwand wieder lohnen.


Der relevante Unterschied zwischen structs und classes ist nur das structs Kopiersemantik verfolgen während classes eben Referenztypen sind. Ansonsten unterscheiden die sich in dem was sie können nicht.
In .Net geht es bei der Entscheidung nehme ich einen Struct oder eine Klasse nur darum ob man Kopiersemantik oder Referenzsemantik will oder nicht. Ich habe die Details von Records in Delphi nicht mehr im Kopf um zu sagen ob die besser als classes oder als structs darzustellen sind. Da sollten sich hier im Forum aber ein paar Grenzgänger finden lassen die genug Erfahrung in beiden Sprachen haben um da zu helfen. Wenn du vorhast den Inhalt des struct während der Lebenszeit des struct zu verändern und nicht nur einmal bei der Erzeugung zu initialisieren würde ich sagen willst du mit höchster Wahrscheinlichleit lieber eine Klasse und keinen struct.
OlafSt Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 486
Erhaltene Danke: 99

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Mi 30.01.13 13:09 
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:

Nein. Du willst (oder zumindest solltest) das Problem auf das dich der Compiler hinweist lösen. Einfach loswerden der Warnung heißt nicht zwingend das man danach funktionierenden Code hat.


Haarspalter ;) Aber du hast recht, ich bin grundsätzlich an einer sauberen Lösung interessiert. Pfuschen kann jeder Depp, aber das kann ich mir in diesem Projekt nicht leisten.

Zitat:
Aber sobald sich dein struct merkwürdig verhält wenn du Hashsets, Dictionaries etc. verwendest solltest du wissen das du an der Stelle noch eine offene Flanke hast die es zu koorigieren gilt.


Ich behalte das im Auge. Man weiß ja nie, wohin einen der Wind weht, bei solchen Projekten.

Zitat:
In .Net geht es bei der Entscheidung nehme ich einen Struct oder eine Klasse nur darum ob man Kopiersemantik oder Referenzsemantik will oder nicht. Ich habe die Details von Records in Delphi nicht mehr im Kopf um zu sagen ob die besser als classes oder als structs darzustellen sind. Da sollten sich hier im Forum aber ein paar Grenzgänger finden lassen die genug Erfahrung in beiden Sprachen haben um da zu helfen. Wenn du vorhast den Inhalt des struct während der Lebenszeit des struct zu verändern und nicht nur einmal bei der Erzeugung zu initialisieren würde ich sagen willst du mit höchster Wahrscheinlichleit lieber eine Klasse und keinen struct.


Ah, nun wird mir verschiedenes klar. Danke für diese Aufklärung !

Da die Daten in den Records aus einer Binärdatei stammen, die auch noch ein flexibles Format hat, boten sich bei der Erstimplementierung in Delphi natürlich records an. Diese lassen sich sehr einfach aus Binärfiles lesen und liegen sofort "ausgepackt" vor einem. das ist in C# erheblich aufwändiger (aber, wie ich bemerkt habe, keinen Jota langsamer).
Die Dateien werden einmal gelesen und die Daten werden in structs/classes gepackt, in einer Liste gesammelt und dann in einen SQL-Server gepumpt. Abgesehen von dem nun aufgekommenen Vergleichsproblem (es können leider Binärfiles mit identischem Inhalt vorliegen), passiert nichts weiter mit den Daten. Angesichts dieses sehr simplen Falls spielt es eigentlich gar keine Rolle, ob structs oder classes.

Ich werde also den "Fix" von @Palladin007 anwenden und beobachten, ob das ganze noch so läuft wie es soll. Wenn nicht, komme ich nochmal hierher zurück.

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4708
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 30.01.13 13:23 
Zitat:
das ist in C# erheblich aufwändiger (aber, wie ich bemerkt habe, keinen Jota langsamer).


Mit dem BinaryFormatter ein klackst. Der schreibt dir auch einen ganzen Objektgraphen weg. Hilft natürlich nicht wenn du weiterhin das Delphi Binärformat lesen must?