Metoda MemberwiseClone

Jak wiemy, każdy obiekt, zarówno reference jak i value, pochodzi pośrednio\bezpośrednio od klasy object. Nie chce omawiać szczegółów ale jedną z protected metod dostarczonych przez object jest właśnie MemberwiseClone. Do czego ona służy?

W skrócie: do wykonania tzw. płytkiej kopii (shallow copy). MemberwiseClone kopiuje obiekt pole po polu. Niestety jest to płytka kopia więc jak jakiś obiekt zawiera referencje do innych obiektów wtedy jedynie adres zostanie sklonowany a nie całe drzewo obiektów. Z typami value nie ma takich problemów bo są one kopiowane bit po bicie (int, float, structs etc).

Jeśli płytka kopia nam wystarczy, wtedy z MemberwiseClone jest to bardzo proste. Jak już wspomniałem, jest to chroniona metoda więc aby z niej skorzystać należy ją w jakiś sposób wyeksponować. Dobrym zwyczajem jest implementacja interfejsu ICloneable:

class Person:ICloneable
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age{get;set;}
    public Company Company { get; set; }
    
    public object Clone()
    {
        return MemberwiseClone();
    }
}
class Company
{
    public string Name { get; set; }
}

Następnie zróbmy mały test:

var person = new Person {FirstName = "Piotr", LastName = "Zielinski",Age = -1};
person.Company = new Company {Name = "GE"};

var newPerson = (Person)person.Clone();
newPerson.LastName=newPerson.FirstName = "new";
newPerson.Company.Name = "new";
newPerson.Age = -5;

Debug.Assert(newPerson.FirstName!=person.FirstName);
Debug.Assert(newPerson.LastName != person.LastName);
Debug.Assert(newPerson.Age != person.Age);

Debug.Assert(newPerson.Company.Name == person.Company.Name);

Zgodnie z definicją shallow copy, powinniśmy zaobserwować następujące zachowanie:

  1. Zmiana FirstName, LastName, Age powinna dotyczyć tylko nowego obiektu. Pomimo, że string to klasa a nie struktura, to zachowuje się jak zwykły value type.
  2. Company jest typem referencyjnym zatem został skopiowany wyłącznie adres. A co za tym idzie, zmiana newPerson.Company.Name spowoduje zmianę również person.Company.Name ( to ten sam obiekt).

3 thoughts on “Metoda MemberwiseClone”

  1. `Dobrym zwyczajem jest implementacja interfejsu ICloneable` – ciekawe że o tym wspominasz, bo z ICloneable jest trochę problemów.

    Mianowicie, jego użycie jest odradzane.

    Dlaczego? Ponieważ /nie zostało sprecyzowane/ czy ICloneable ma tworzyć płytką czy głęboką kopię, co jest trochę bezsensowne, ponieważ w wyniku nie nadaje się ani do tego ani do tego (co to za interfejs o którym nie można powiedzieć co robi?). Jest to uważane trochę za błąd przy projektowaniu języka.

    Cóż, ostatecznie nie zdobyto się na konkretne kroki względem tego interfejsu, poza ‘odradzaniem’. Coś więcej na ten temat na przykład tutaj: http://blogs.msdn.com/b/brada/archive/2004/05/03/125427.aspx

  2. W przypadku ICloneable jest jeszcze jeden problem. Jeżeli po klasie Person dziedziczy User to wywołując Clone na User nie jesteśmy pewni czy zwrócony obiekt jest typu User czy Person (jeżeli programista użyje MemberwiseClone to nie ma problemu. Ale nie mamy gwarancji że jej użył

Leave a Reply

Your email address will not be published.