Taka długa odpowiedź na moje posty wynikłe wyłącznie z odreagowywania frustracji wywołanej siedzieniem któryś dzień do białego rana...!!! Szacun, dzięki!!
Ja też nie wiem czemu tak odpisałem. Odreagowywałem frustracje wynikającą z siedzenia w pracy do późna :P.
Tak, naprawdę napisałem taki kod. Dlaczego? Bo tak dobrze radziła / kazała dokumentacja: https://msdn.microsoft.com/en-us/library/dd183755.aspx
Nie do końca, bo największa wada Twojego kodu to to, że praktycznie skopiowałeś metodę Equals do == - nawet w przykładzie który podałeś, operator== po prostu woła Equals. Dzięki temu nie ma kopiowania kodu.
...i w sumie to zrobiłem chyba buga w kodzie, bo zrobienie == w operator== to głupi pomysł (po to te object.ReferenceEquals są). No ale to by wyszło jakbym go chociaż uruchomił.
np. serializacja; XMLSerializer wymaga, by wszystkie pola poddane serializacji były publiczne; ale nie wszystko można od razu zserializować, więc niektóre pola istnieją tylko na potrzeby serializacji i nie mają być odczytywane przez inne klasy; więc w końcu stawiam _ przed nazwą pola żeby pokazać że semantycznie jest ono prywatne mimo że jest publiczne... Co robię źle?
Faktycznie, XMLSerializer patrzy tylko na publcizne pola. Odnośnie tego co z tym zrobić, jest kilka opcji - możesz np. zaimplementować ręcznie IXmlSerializable, ale to Ci się nie spodoba i będziesz narzekał że C# zły bo trzeba ręcznie kod pisać :P (chociaż tbh w C++ by było z tym dużo gorzej, przez brak sensownej refleksji). Możesz tez użyć DataContractSerializer
czy jak się to nowe coś nazywa. Albo po prostu serializować binarnie. Jeszcze jest coś SOAPowego, ale nie kojarzę nazwy (ani czy się nada).
Albo możesz użyć serializacji binarnej.
W zasadzie może i masz rację. Teraz myślę, że chyba rzeczywiście się zagalopowałem. Zaczyna mi się wydawać, że spośród na oko jakiś pięciu takich głębokich porównań jakie zaimplementowałem bodajże tylko jedno było mi naprawdę potrzebne.
Być może dwa, ale to już wymaga zastanowienia.
:>
Rzeczywiście... Dopiero musiałem sprawdzić naocznie, co mi rzucało.
Natomiast faktycznie zdarzyło mi się już sporo razy zasypać się NullReferenceException z powodu odruchowego nieinicjalizowania pól. Teraz już powoli się tego uczę...
De facto problem jest taki, że jeśli masz klasę Foo, i napiszesz w C#:
Foo x;
to jest odpowiednikiem (w uproszczeniu) takiego kodu w C++:
Foo *x
Jeśli dokonasz takiego mentalnego podstawienia w głowie, to wszystko zaczyna działać logicznie (znając w C++). Nie odnosi się to do struktur, jeśli masz struct Bar
, to Bar x
w C# dalej działa podobnie jak Bar x
w C++.
To mógłbyś wszystkie te pozostałe metody sam zaimplementować; SimpleEquals pomyślałem jako skrót dla 90% zastosowań, gdzie nie jest to konieczne.
Gdyby tylko w C# były traity... Da się to dohackować trochę na siłę extension methodami, ale ładne to nie będzie.
tzn. byś pisał:
class Test : ISimpleEquals
{
public override bool Equals(object o) { return this.CheckSimpleEquals(o); }
public bool SimpleEquals(Test t) { ... }
}
gdzie CheckSimpleEquals to extension method na ISimpleEquals. Bez traitów nie da sie tego niestety zrobić bez nadpisywania metody Equals.
Ewentualnie możesz jeszcze zrobić klasę bazowa na to, którą będzie można zastosować w 99% przypadków:
abstract class SimpleEquals : ISimpleEquals
{
public override bool Equals(object o) { return this.CheckSimpleEquals(o); }
abstract bool SimpleEquals(Test t);
}
OKOK a dlaczego? Bo tak lepiej semantycznie czy tak łatwiej zaimplementować?
Zaimplementowąć tak samo trudno, semantycznie w sumie chyba podobnie.
Jest tak lepiej dlatego, że w standardowym przypadku, kiedy porównujesz po wszystkich polach, zamiast wrzucać 30 atrubutów to Twojej klasy z danymi, będziesz dodawać zero. Czasami kiedy trzeba coś ignorować, będziesz dodawać jeden. No i dzięki temu od razu zadziała Ci takie porównanie z klasami zewnętrznymi, gdzie autor nie wiedział o tym atrybucie (wtedy będzie działało jak standardowe głębokie porównanie, po wszystkich polach).
Jak ten mój pomysł zaimplementować przyznam się że nie wiem, bo nie czytałem jeszcze o definiowaniu własnych atrybutów i wszystkim z tym związanym... I chwilowo raczej nie poczytam bo nie jest mi to na razie potrzebne. Ale nie, nie pytam Cię w ten sposób, jak to konkretnie zaimplementować, aż tak leniwy nie jestem :) Sam to może kiedyś zrobię.
Wiem że nie pytałeś :P. Po prostu to brzmi jak fajne ćwiczenie na atrybuty C#owe, i ich praktyczne zastosowanie. No i refleksję. A i może taki kod sie kiedyś gdzieś przyda.
A dlaczego tak? Bo (kolejny WTF) - nie ma pól poza klasą.
To akurat bardzo celowa decyzja projektowa. Jeśli powiesz słowo "zmienne globalne" na spotkaniu programistów C#/Javy, to zostaniesz prawdopodobnie zjedzony :P.
Mam sobie klasę UserData na przechowywanie takich rzeczy jak np. awatara gracza. Przecież do dana globalna, semantycznie rzecz ujmując. No ale to się oczywiście może zmieniać... Tylko tak jak powyżej można napisać UserData.avatar, a to już jest wygodne. Dlaczego UserData to nie może być obiekt globalny...?
Prosta odpowiedź: bo C# nie wspiera obiektów globalnych.
Skomplikowana odpowiedź: odkryj wzorzec https://en.wikipedia.org/wiki/Singleton_pattern :P.
Jeszcze bardziej skomplikowana odpowiedź: z czasem odkryjesz (pradopodobnie) że coraz mniej statycznych rzeczy jest Ci potrzebne.
W praktyce prawie zawsze jest jakiś kontekst, który jest lepszym miejscem na przechowywabnie danych niż globalnie. Przykładowo możesz mieć klasę Game, a w niej instancję klasy UserData - wtedy w klasie Game odwołujesz się do userdata po prostu jak do pola. (jak to dokładnie powino wyglądać w Twoim przykładzie, ciężko powiedzieć bez znajomości kodu).
Ot, C# był projektowany pod bardziej Enterprise
aplikacje, a tam zmienne globalne to zawsze zły pomysł.
Ten temat to - czemu c# to nie c++ ?
E, bez przesady, bardziej "czym się różni C# od C++ i jak zmienić nawyki w związku z tym" :P.
Rozumiem jakbyście z javą albo dartem porównywali , np dart ma fajny shortcut do konstruktora
Constructor(this.fst, this.scd);
w c# by się taki zdał i często by oszczędził przepisywania this.fst = _fst.
Speak no more :P
https://msdn.microsoft.com/en-us/library/k6sa6h87.aspx
public CoOrds(int x, int y)
{
this.x = x;
this.y = y;
}
Czytałem kiedyś takie żale C vs Pascal. Autor czepiał się dosłownie każdej różnicy (na korzyść C); a to że begin/end zamiast klamerek, a to że komentarze w klamerkach, a to braku średnika przed else i t.p.
Chyba też czytałem ten artykuł. Albo przynajmniej podobny. Jak wolę C od Pascala (po części dlatego że go lepiej znam), to artykuł był strasznie słabo napisany - na zasadzie "w C jest inaczej, więc Pascal jest gorszy".
Ale tutaj wydaje mi się że jest trochę inny problem, bo C jest podobniejszy do Pascala niż C++ do C# - wbrew pozorom różnicę Pascal <-> C są w dużej części składniowe, a C++ vs C# to kompletnie inna filozofia.