C# ICloneable

C# ve .NET içerisindeki dillerde nesne kopyalama işlemini shallow copy veya deep copy olarak yapmayı standart hale getiren ICloneable arayüzü ile ilgili bilgiler yer alıyor.

Nesne kopyalama nedir?

Daha önceden oluşturulan nesnenin bir kopyasını başka bir bellek alanına alma işlemine nesne kopyalama denir.

C# içerisinde temel olarak veri türleri değer ve referans olmak üzere ikiye ayrılır.

Değer türleri belleğin kısıtlı olduğu stack alanında yer alır ve kopyalama işlemi eşittir (=) ile yapılarak bit-bit kopyalanır.

using System;

class Program {

    static void Main() {

        int a = 1453;
        Console.WriteLine(a);

        int b = a;
        Console.WriteLine(b);

        b = 55;  // değişiklik yapıldı
        Console.WriteLine(a);
        Console.WriteLine(b);
    }
}

Ancak bu durum referans türlerinde farklıdır.

using System;

class Program {

    static void Main() {

        Sinif a = new Sinif(1453);
        Console.WriteLine(a.Sayi);

        Sinif b = a;
        Console.WriteLine(b.Sayi);

        b.Sayi = 55;  // değişiklik yapıldı
        Console.WriteLine(a.Sayi);
        Console.WriteLine(b.Sayi);
    }
}

class Sinif {
    public int Sayi;

    public Sinif(int sayi) {
        this.Sayi = sayi;
    }
}

Referans türleri eşittir (=) yapıldığında sadece adres alanını kopyalanır.

Adres alanı kopyalandığından yapılan değişiklik tüm nesneler üzerinde geçerli olacaktır.

Bu durumun önüne geçebilmek için bir metot hazırlayıp yeni bir nesne oluşturulabilir.

using System;

class Program {

    static void Main() {

        Sinif a = new Sinif(1453);
        Console.WriteLine(a.Sayi);

        Sinif b = a.Kopyala();
        Console.WriteLine(b.Sayi);

        b.Sayi = 55;  // değişiklik yapıldı
        Console.WriteLine(a.Sayi);
        Console.WriteLine(b.Sayi);
    }
}

class Sinif {
    public int Sayi;

    public Sinif(int sayi) {
        this.Sayi = sayi;
    }

    public Sinif Kopyala() {
        return new Sinif(this.Sayi);
    }
}

.NET kopyalama işlemini standart hale getirmek için ICloneable arayüzü yer almaktadır.

Bu arayüz uygulandığında object türüne sahip Clone metodunun tanımlanması gerekir.

using System;

class Program {

    static void Main() {

        Sinif a = new Sinif(1453);
        Console.WriteLine(a.Sayi);

        Sinif b = a.Clone() as Sinif;
        Console.WriteLine(b.Sayi);

        b.Sayi = 55;  // değişiklik yapıldı
        Console.WriteLine(a.Sayi);
        Console.WriteLine(b.Sayi);
    }
}

class Sinif : ICloneable {
    public int Sayi;

    public Sinif(int sayi) {
        this.Sayi = sayi;
    }

    public object Clone() {
        return new Sinif(this.Sayi);
    }
}

Bu kullanımda nesne kopyalama işlemi için bir kurucu metot kullanılmıştır.

Birden fazla özelliği olan sınıflarda bu durum karmaşıklığa neden olacağından kendi sınıf türünden parametre alan kopyalayıcı kurucu tanımlamak faydalı olacaktır.

using System;

class Program {

    static void Main() {

        Sinif a = new Sinif(1453);
        Console.WriteLine(a.Sayi);

        Sinif b = a.Clone() as Sinif;
        Console.WriteLine(b.Sayi);

        b.Sayi = 55;  // değişiklik yapıldı
        Console.WriteLine(a.Sayi);
        Console.WriteLine(b.Sayi);
    }
}

class Sinif : ICloneable {
    public int Sayi;

    public Sinif(int sayi) {
        this.Sayi = sayi;
    }

    public Sinif(Sinif nesne) {
        this.Sayi = nesne.Sayi;
    }

    public object Clone() {
        return new Sinif(this);
    }
}

Nesne kopyalama işlemini ayrıca object sınıfında yer alan MemberwiseClone metodu ile de yapılabilir.

using System;

class Program {

    static void Main() {

        Sinif a = new Sinif(1453);
        Console.WriteLine(a.Sayi);

        Sinif b = a.Clone() as Sinif;
        Console.WriteLine(b.Sayi);

        b.Sayi = 55;  // değişiklik yapıldı
        Console.WriteLine(a.Sayi);
        Console.WriteLine(b.Sayi);
    }
}

class Sinif : ICloneable {
    public int Sayi;

    public Sinif(int sayi) {
        this.Sayi = sayi;
    }

    public object Clone() {
        return this.MemberwiseClone();
    }
}

Aslında biz bir sınıf oluşturduğumuzda arkada object sınıfında yer alan MemberwiseClone ile sınıf içerisinde yer alan metotlar sınıfa kopyalanır.

Böylece yeni oluşturulan sınıf içerisinde olmamasına rağmen GetType, ToString gibi metotlar yer alır.

Bu kopyalama işlemi shallow copy yani sığ-yüzeysel kopyalama olarak adlandırılmaktadır.

Çünkü kopyalama işlemi sadece sınıf içerisinde yer alan değer türleri için yapılmaktadır.

Sınıf içerisinde yer alan referans türlerinin bellek adresi kopyalanmaktadır.

using System;

class Program {

    static void Main() {

        Sinif a = new Sinif(1453);
        Console.WriteLine(a.Sayi);
        Console.WriteLine(a.ReferansTipi.Yazi);

        Sinif b = a.Clone() as Sinif;
        Console.WriteLine(b.Sayi);
        Console.WriteLine(b.ReferansTipi.Yazi);

        b.Sayi = 55;
        b.ReferansTipi.Yazi = "XYZ";  // değişiklik yapıldı
        Console.WriteLine(a.Sayi);
        Console.WriteLine(a.ReferansTipi.Yazi);
        Console.WriteLine(b.Sayi);
        Console.WriteLine(b.ReferansTipi.Yazi);
    }
}

class Sinif : ICloneable {
    public int Sayi;
    public BaskaSinif ReferansTipi;

    public Sinif(int sayi) {
        this.Sayi = sayi;
        this.ReferansTipi = new BaskaSinif("ABC");
    }

    public object Clone() {
        return this.MemberwiseClone();
    }
}

class BaskaSinif {
    public string Yazi;

    public BaskaSinif(string yazi) {
        this.Yazi = yazi;
    }
}

Bu durumun önüne geçebilmek için deep copy olarak adlandırılan derin-keskin kopyalama işleminin yapılması gerekir.

Bu işlem için her sınıfın kopyalama işlemini yapıyor olması veya serileştirme yöntemi ile yapılmaktadır.

using System;

class Program {

    static void Main() {

        Sinif a = new Sinif(1453);
        Console.WriteLine(a.Sayi);
        Console.WriteLine(a.ReferansTipi.Yazi);

        Sinif b = a.Clone() as Sinif;
        Console.WriteLine(b.Sayi);
        Console.WriteLine(b.ReferansTipi.Yazi);

        b.Sayi = 55;
        b.ReferansTipi.Yazi = "XYZ";  // değişiklik yapıldı
        Console.WriteLine(a.Sayi);
        Console.WriteLine(a.ReferansTipi.Yazi);
        Console.WriteLine(b.Sayi);
        Console.WriteLine(b.ReferansTipi.Yazi);
    }
}

class Sinif : ICloneable {
    public int Sayi;
    public BaskaSinif ReferansTipi;

    public Sinif(int sayi) {
        this.Sayi = sayi;
        this.ReferansTipi = new BaskaSinif("ABC");
    }

    public object Clone() {
        Sinif yeniSinif = (Sinif)this.MemberwiseClone();
        yeniSinif.ReferansTipi = (BaskaSinif)this.ReferansTipi.Clone();
        return yeniSinif;
    }
}

class BaskaSinif : ICloneable {
    public string Yazi;

    public BaskaSinif(string yazi) {
        this.Yazi = yazi;
    }

    public object Clone() {
        return this.MemberwiseClone();
    }
}

Görüldüğü üzere nesne kopyalama işlemi sınıf içerisindeki tüm elemanlar için yapılmıştır.

Bu yöntem sınıf içerisinde birden fazla eleman olduğu durumda fazla kod yazımına neden olacağından serileştirme işlemi ile yapılabilir.

Ayrıca Clone metodunun dönüş türü object türünden olduğunda ek tür dönüşümüne ihtiyaç duyulmaktadır.

ICloneable arayüzünü kullanmak yerine kendi belirlemiş olduğunuz kopyalama metodunu kullanmak performans açısından faydalı olacaktır.

C# Derslerine buradan ulaşabilirsiniz.

Hayırlı günler dilerim.

Yusuf SEZER

Yusuf SEZER

Computer Engineer who interested about web technologies, algorithms, artificial intelligence and embedded systems; constantly exploring new technologies.


Bunlara'da bakmalısın!