Clone i Equals oparte na serializacji nie działają

0

Mam w swoim programie klasę Store, która jest dość rozbudowana - zawiera zarówno pola typów wartościowych, jak i innych klas, które z kolei znowu składają się z pól wartościowych i referencyjnych... Takie "drzewko" jest dość duże i ma jak na razie 5 poziomów zagnieżdżenia.

Potrzebuję w programie utworzyć kopię obiektu mojej klasy głównej i zachować ją w pamięci, aby następnie móc ją porównać z innym obiektem tej klasy.

Wobec tego postanowiłem zaimplementować metody Object.Equals i ICloneable.Clone. Ale chciałbym, żeby było to po pierwsze sprawne, po drugie łatwe do rozbudowywania.
Ręczna implementacja tych metod będzie czasochłonna, bo musiałbym to zrobić nie tylko w klasie głównej, ale i we wszystkich, z którymi jest powiązana, a jest ich już sporo. Do tego będą pojawiały się nowe, a w już istniejących na pewno będą pojawiały się nowe pola. Każda taka zmiana wymagałaby ingerencji w treść metod, a tego chcę uniknąć.

Wpadłem na pomysł użycia do tego serializacji binarnej, ale nie chce ona działać. Oto moja metoda kopiująca:

public object Clone()
{
    using (MemoryStream ms = new MemoryStream())
    {
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(ms, this);
        ms.Position = 0;
        return bf.Deserialize(ms);
    }
}

Powinno działać, ale nie działa. Klasa Store ma trzy pola typu string i jedno typu List<Product>. O ile klonowanie dla wypełnionego obiektu działa dobrze, o tyle przy "pustym" (każdy string to "", a lista ma 0 elementów) z obiektu, który zajmuje 803 bajty tworzy kopię, która ma ich 801, więc moja metoda porównująca twierdzi, że są to różne obiekty.

Dlaczego?

Tu metoda Equals (również oparta na serializacji binarnej), na wszelki wypadek:

public override bool Equals(object obj)
{
    Store second = obj as Store;

    using (MemoryStream myStream = new MemoryStream(), secStream = new MemoryStream())
    {
        BinaryFormatter bin = new BinaryFormatter();
        bin.Serialize(myStream, this);
        bin.Serialize(secStream, obj);

        if (myStream.Length != secStream.Length)
            return false;

        byte[] myBytes = myStream.ToArray();
        byte[] secBytes = secStream.ToArray();

        for (int i = 0; i < myBytes.Length; i++)
            if (myBytes[i] != secBytes[i])
                return false;

        return true;
    }
}

Aha - próby użycia w analogiczny sposób serializacji XML kończą się StackOverException.

0

Muszę Ci powiedzieć, że u siebie też tak klonuje obiekty i z ciekawości sprawdziłem Twoją metodę Equal i u mnie działa dobrze. Może więc przyczyna leży w klasie Store ?? ale to by było dziwne. ciekawe hmm

0

Tylko co w tej klasie Store, w końcu ona jest tutaj całkowicie bierna?

0

Nie wiem co tu się dzieje to tzw UFO jeśli o Ciebie nie działa a u mnie ten sam kod śmiga. Przekleiłem toćka w toćke to co masz w poście i działa za każdym razem. Wiec jeśli metody działają na moich obiektach to nie przychodzi mi nic innego do głowy jak to, że u Ciebie błąd powoduje klonowanie obiektu innej klasy niż ta na której sprawdzałem Twoją metodę Equals ? Chociaż to idiotyczne i mogłoby jedynie wskazywać na jakiś bug w BinaryFormatter ??

0

No tak, w innym projekcie, w innej klasie to działa... :/

A nie masz innego pomysłu na proste sklonowanie całego obiektu?

//dopisane:

Dobra, obszedłem to w ten sposób, że binarne Clone wrzuciłem do klasy Product, a Clone w Store działa "ręcznie".
Niemniej jednak nadal nie rozumiem, czemu nie działało dobrze, jedynym sensownym dla mnie wyjaśnieniem jest bug w .NET.

0
somekind napisał(a)

A nie masz innego pomysłu na proste sklonowanie całego obiektu?

Może poniższe artykuły Cię zainteresują:

  1. http://whizzodev.blogspot.com/2008/03/object-cloning-using-il-in-c.html
  2. http://whizzodev.blogspot.com/2008/06/object-deep-cloning-using-il-in-c.html
0

Dzięki wielkie :) Z akcentem na "proste" ;)

Przez IL to w ogóle hardcore jak dla mnie, próbowałem już wcześniej refleksji w połączeniu z właściwościami będącymi kolekcjami generycznymi i sypało się w run-time. Ponieważ nie mam chwilowo czasu na ogarnianie żadnej z tych dwóch opcji zostaję przy kombinowanym rozwiązaniu, które zajmuje całe 10 linijek kodu, działa i mam nadzieję działać będzie :)

0

http://www.csharp411.com/c-object-clone-wars/ tu gość opisał chyba wszystkie możliwe łącznie z IL kolegi wyżej. Innych możliwości chyba nie ma. A co do najprostszej metody to jednak serializacja jest taka :P. Dalej jestem ciekaw jaki Typ/struktura po serializacji i deserializacji nie jest sobie równy - gdybyś to wytropił byłbym wdzięczny za informacje

0

Na tym artykule wzorowałem się tworząc swój kod. Szkoda tylko, że nie działa on tak, jak właściwie powinien.

Nie są sobie równe główne obiekty logiki mojego programu, nagle w środku strumienia po serializacji znajdują się jakieś dodatkowe bajty. Ale dane w obiekcie cały czas są takie same - to chyba te cuda, o których mówił premier ;).

Masz pomysł jak to wytropić?

0

jak chcesz czegoś szukać to najlepiej binarniy podział :P bierzesz swoją strukturę i wywalasz z niej połowę kodu :D i serializaujesz/deserializujesz ... jak działa tzn że błąd był w drugiej połowie ... błędną połowę kroisz na pół itd.
W końcu znajdziesz która klasa/typ się wywala.

a skąd może się brać błąd to ja nie wiem ... może jakieś pola powinieneś oznaczyć jako Nonserializable ??

a jeśli chodzi o klonowanie przez refleksję to dało by się to zrobić tylko że pewnie będzie to deko wolniej działać niż serializacja.

1 użytkowników online, w tym zalogowanych: 0, gości: 1