Neden ICloneable <T> yok?


223

Jenerik ICloneable<T>olmamasının özel bir nedeni var mı?

Çok daha rahat olurdu, eğer bir şeyi klonladığım her şeyi yapmam gerekmiyorsa.


6
Hayır! 'gerekçelere' tüm saygımla, sana katılıyorum, bunu uygulamalıydılar!
Shimmy Weitzhandler

Microsoft'un tanımlaması iyi bir şey olurdu (kendi demlemek arabirimlerindeki sorun, iki farklı montajdaki arabirimlerin anlamsal olarak aynı olsalar bile uyumsuz olacağıdır). Arayüzü tasarlasaydım, üç üye olurdu, Clone, Self ve CloneIfMutable, hepsi bir T döndürür (son üye uygun şekilde Clone veya Self'i döndürür). Kendi kendine üye ICloneable (Foo) 'yu parametre olarak kabul etmeyi ve daha sonra onu tipik bir tahmin gerektirmeden Foo olarak kullanmayı mümkün kılacaktır.
Supercat

Bu, kalıtsal sınıfların korumalı bir "klon" yöntemini ortaya çıkardığı ve halka açık bir yöntemi ortaya çıkaran türevlere sahip olduğu uygun bir klonlama sınıfı hiyerarşisine izin verecektir. Örneğin, Pile, CloneablePile: Pile, EnhancedPile: Pile ve CloneableEnhancedPile: EnhancedPile öğelerine sahip olabilir, bunların hiçbiri klonlanırsa (tümü genel bir klonlama yöntemini göstermese de kırılacaktır) klonlanırsa, ancak herhangi bir klonlama yöntemi göstermez). ICloneable (Kazık) kabul eden bir rutin, bir CloneablePile veya CloneableEnhancedPile kabul edebilir ...
supercat

... CloneableEnhancedPile, CloneablePile öğesinden devralmasa bile. EnhancedPile, CloneablePile'den devralındıysa, MoreEnhancedPile'ın genel bir klonlama yöntemini açığa çıkarması gerektiğini ve Liskov Değiştirilebilirlik İlkesini ihlal ederek Klonlamayı bekleyecek koda geçirilebileceğini unutmayın. CloneableEnhancedPile, ICloneable (Of EnhancedPile) uygulayacağından ve ICloneable (Of Pile) uygulamasından dolayı, klonlanabilir bir Pile türevini bekleyen bir rutine aktarılabilir.
Supercat

Yanıtlar:


111

ICloneable şu anda kötü bir API olarak kabul edilmektedir, çünkü sonucun derin mi yoksa sığ bir kopya mı olduğunu belirtmemektedir. Bence bu yüzden bu arayüzü geliştirmiyorlar.

Muhtemelen yazılan bir klonlama uzantısı yöntemi yapabilirsiniz, ancak uzantı yöntemleri orijinal olanlardan daha az önceliğe sahip olduğundan farklı bir ad gerektireceğini düşünüyorum.


8
Kabul etmiyorum, kötü ya da kötü değil, SHALLOW klonlaması için çok yararlı ve bu durumlarda gereksiz bir boksu ve kutuyu kurtarmak için gerçekten Klon <T> 'ye ihtiyaç var.
Shimmy Weitzhandler

2
@AndreyShchekin: Derin ve sığ klonlama hakkında belirsiz olan nedir? Eğer List<T>bir klon yöntemi vardı, ben bir verim beklenir List<T>Öğeleri orijinal listede aynı kimlikleri var, ama ben etkileyecek bir listeye yapılan hiçbir şeyin sağlamak için gerektiği gibi herhangi bir iç veri yapıları çoğaltılamaz olacağını beklenebilir kimlikler öğelerin diğer saklanan. Belirsizlik nerede? Klonlama ile ilgili daha büyük bir sorun, "elmas sorununun" bir varyasyonu ile birlikte gelir: CloneableFoo[halka açık olarak klonlanamaz] 'dan miras alınırsa Foo, CloneableDerivedFoo...
supercat

1
@supercat: Beklentinizi tam olarak anladığımı söyleyemem identity, örneğin listenin kendisini neyi dikkate alacağınız gibi (listelerin listesi durumunda)? Ancak, bunu göz ardı ederek, beklentileriniz insanların arama veya uygulama sırasında sahip olabileceği tek fikir değildir Clone. Başka bir liste uygulayan kütüphane yazarları beklentilerinizi karşılamazsa ne olur? API önemsiz derecede açık olmalı, tartışmasız açık olmamalıdır.
Andrey Shchekin

2
@supercat Yani sığ kopyadan bahsediyorsunuz. Bununla birlikte, örneğin bellek içi nesnelerde tersinir bir işlem yapmak istiyorsanız, derin bir kopyada temelde yanlış bir şey yoktur. Beklentiniz kullanım durumunuza dayanmaktadır, diğer insanların farklı beklentileri olabilir. Nesnelerinizi nesnelerine koyarlarsa (veya tam tersi), sonuçlar beklenmedik olur.
Andrey Shchekin

5
@supercat, uygulamanızın bir parçası değil, .NET çerçevesinin bir parçası olduğunu dikkate almazsınız. derin klonlama yapmayan bir sınıf yaparsanız, başka biri derin klonlama yapan ve Clonetüm parçalarını çağıran bir sınıf yaparsa , bu parçanın sizin tarafınızdan mı yoksa o kişi tarafından mı uygulandığına bağlı olarak tahmin edilemez derin klonlamayı seven örüntüler hakkındaki noktanız geçerlidir, ancak API'de IMHO'ya sahip olmak yeterince açık değildir - ya ShallowCopykonuyu vurgulamak için çağrılmalı ya da hiç verilmemelidir.
Andrey Shchekin

141

Andrey bu cevabına ek olarak (benim de katıldığım, 1) - zaman ICloneable olduğu yapılan, ayrıca halka açık hale getirmek açık uygulanmasını seçebilir Clone()dönüşü Yazılan nesnesi:

public Foo Clone() { /* your code */ }
object ICloneable.Clone() {return Clone();}

Elbette jenerik ile ilgili ikinci bir sorun var ICloneable<T> - kalıtımla .

Sahip olursam:

public class Foo {}
public class Bar : Foo {}

Ve uyguladım ICloneable<T>, sonra uygularım ICloneable<Foo>mı? ICloneable<Bar>? Hızla birçok özdeş arabirim uygulamaya başlıyorsunuz ... Bir oyuncu kadrosu ile karşılaştırın ... ve gerçekten çok mu kötü?


10
+1 Her zaman kovaryans sorununun gerçek sebep olduğunu düşündüm
Tamas Czinege

Bununla ilgili bir sorun var: Açık arabirim uygulaması özel olmalı , bu da sorunlara neden olacak Foo'nun bir noktada ICloneable'a dökülmesi gerekiyor ... Burada bir şey mi kaçırıyorum?
Joel in Gö

3
Benim hatam: Özel olarak tanımlanmasına rağmen, Foo'nun IClonable'a dökülmesi gerektiği yöntemin ortaya çıkacağı ortaya çıktı (CLR, C # 2nd Ed, s.319). Tuhaf bir tasarım kararı gibi görünüyor, ama işte burada. Yani Foo.Clone () ilk yöntemi verir ve ((ICloneable) Foo) .Clone () ikinci yöntemi verir.
Joel in Gö

Aslında, açık arayüz uygulamaları, açık bir şekilde ifade etmek gerekirse, herkese açık API'nın bir parçasıdır. Böylece döküm yoluyla alenen çağrılabilirler.
Marc Gravell

8
Önerilen çözüm ilk başta harika görünüyor ... bir sınıf hiyerarşisinde, 'public Foo Clone ()' in türetilmiş sınıflarda sanal ve geçersiz kılınmasını istediğinizi fark edene kadar, kendi türlerini döndürebilirler (ve kendi alanlarını klonlayabilirler) elbette). Ne yazık ki, geçersiz kılmadaki dönüş türünü değiştiremezsiniz (belirtilen kovaryans sorunu). Böylece, tekrar orijinal soruna geri dönüyorsunuz - açık uygulama sizi gerçekten fazla satın almıyor (hiyerarşinizin temel türünü 'nesne' yerine döndürmek dışında) ve Arama sonuçlarını klonlayın (). Yuck!
Ken Beckett

18

Sormak zorundayım, arabirimi uygulamak dışında tam olarak ne yapardınız ? Arayüzler tipik olarak sadece ona yayın yaptığınızda (yani, bu sınıf 'IBar'ı destekliyorsa) ya da onu alan parametreler veya ayarlayıcılara sahip olduğunda (yani bir' IBar 'alırım) kullanışlıdır. ICloneable ile - tüm Çerçeveyi inceledik ve uygulanmasından başka bir yerde tek bir kullanım bulamadık. Ayrıca 'gerçek dünyada' onu uygulamaktan başka bir şey yapan herhangi bir kullanım bulamadık (eriştiğimiz ~ 60.000 uygulamada).

Şimdi 'klonlanabilir' nesnelerinizin uygulanmasını istediğiniz bir deseni uygulamak istiyorsanız, bu tamamen iyi bir kullanımdır ve devam edin. Ayrıca "klonlamanın" sizin için ne anlama geldiğine de karar verebilirsiniz (yani derin veya sığ). Ancak, bu durumda, onu (BCL) tanımlamamıza gerek yoktur. BCL'deki soyutlamaları yalnızca ilişkisiz kütüphaneler arasında soyutlama olarak yazılan örneklerin değiş tokuşuna ihtiyaç duyulduğunda tanımlarız.

David Kean (BCL Takımı)


1
Jenerik olmayan ICloneable çok yararlı değildir, ancak ICloneable<out T>miras alındığında ISelf<out T>tek bir Selftür yöntemle oldukça yararlı olabilir T. Kişi genellikle "klonlanabilir bir şeye" ihtiyaç duymaz, fakat klonlanabilir bir şeye çok iyi ihtiyaç Tduyabilir. Klonlanabilir bir nesne uygulanırsa ISelf<itsOwnType>, klonlanabilir olana ihtiyaç duyan bir rutin, ortak bir atayı paylaşabilecek tüm klonlanabilir türevleri olmasa bile bir Ttür parametresini kabul edebilir . ICloneable<T>T
Supercat

Teşekkürler. Bu, yukarıda bahsettiğim döküm duruma benzer (yani 'bu sınıf' IBar'ı destekliyor mu?) Ne yazık ki, sadece T'nin klonlanabilir olduğu gerçeğinden gerçekten yararlanacağınız çok sınırlı ve izole senaryolar geliştirdik. Aklınızdaki durumlar var mı?
David Kean

.Net içinde bazen garip olan bir şey, bir koleksiyonun içeriğinin bir değer olarak görülmesi gerektiğinde değişebilir koleksiyonları yönetmektir (yani, daha sonra koleksiyondaki öğeler kümesini daha sonra bilmek isteyeceğim). Buna benzer bir şey ICloneable<T>bunun için yararlı olabilir, ancak paralel değişken ve değişmez sınıfları korumak için daha geniş bir çerçeve daha yararlı olabilir. Başka bir deyişle, ne tür Fooiçerdiğini görmek gerekir , ancak ne onu mutasyona ne de hiç değişmeyeceğini beklemek için IReadableFoo
kod

... içeriğini tutmak isteyen Foobir ImmutableFookodu kullanabilir MutableFoo. Herhangi bir tür verilen kod IReadableFoodeğişebilir veya değişmez bir versiyona sahip olmalıdır. Böyle bir çerçeve iyi olurdu, ama ne yazık ki işleri genel bir şekilde kurmak için güzel bir yol bulamıyorum. Bir sınıf için salt okunur bir sarıcı yapmanın tutarlı bir yolu olsaydı, böyle bir şey ICloneable<T>, bir sınıfın değişmez bir kopyasını yapmak için birlikte kullanılabilir T'.
Supercat

@supercat List<T>Klonlanmış List<T>orijinal koleksiyondaki tüm aynı nesnelere işaret eden yeni bir koleksiyon olacak şekilde bir klonlamak istiyorsanız , bunu yapmanın iki kolay yolu vardır ICloneable<T>. Birincisi, Enumerable.ToList()eklenti yöntemidir: List<foo> clone = original.ToList();İkincisi ise bir List<T>yapıcıdır IEnumerable<T>: List<foo> clone = new List<foo>(original);Uzantı yönteminin muhtemelen sadece yapıcıyı çağırdığından şüpheleniyorum, ancak bunların her ikisi de istediğinizi yapacak. ;)
CptRobby

12

"Neden" sorusunun gereksiz olduğunu düşünüyorum. Çok yararlı olan, ancak .NET Frameworku temel kitaplığının bir parçası olmayan çok sayıda arabirim / sınıf / vb. Var.

Ancak, esas olarak bunu kendiniz yapabilirsiniz.

public interface ICloneable<T> : ICloneable {
    new T Clone();
}

public abstract class CloneableBase<T> : ICloneable<T> where T : CloneableBase<T> {
    public abstract T Clone();
    object ICloneable.Clone() { return this.Clone(); }
}

public abstract class CloneableExBase<T> : CloneableBase<T> where T : CloneableExBase<T> {
    protected abstract T CreateClone();
    protected abstract void FillClone( T clone );
    public override T Clone() {
        T clone = this.CreateClone();
        if ( object.ReferenceEquals( clone, null ) ) { throw new NullReferenceException( "Clone was not created." ); }
        return clone
    }
}

public abstract class PersonBase<T> : CloneableExBase<T> where T : PersonBase<T> {
    public string Name { get; set; }

    protected override void FillClone( T clone ) {
        clone.Name = this.Name;
    }
}

public sealed class Person : PersonBase<Person> {
    protected override Person CreateClone() { return new Person(); }
}

public abstract class EmployeeBase<T> : PersonBase<T> where T : EmployeeBase<T> {
    public string Department { get; set; }

    protected override void FillClone( T clone ) {
        base.FillClone( clone );
        clone.Department = this.Department;
    }
}

public sealed class Employee : EmployeeBase<Employee> {
    protected override Employee CreateClone() { return new Employee(); }
}

Devralınabilen bir sınıf için uygulanabilir herhangi bir klon yöntemi, başlangıç ​​noktası olarak Object.MemberwiseClone kullanmalıdır (veya yansıma kullanır), aksi takdirde klonun orijinal nesne ile aynı tür olacağının garantisi yoktur. Eğer ilgilenirseniz oldukça güzel bir desenim var.
supercat

@supercat neden cevap vermiyorsun?
nawfal

"Çok yararlı olan ancak .NET Frameworku temel kütüphanesinin bir parçası olmayan çok sayıda arabirim / sınıf / vb. Var." Bize örnek verebilir misiniz?
Krythic

1
@Krythic ie: b-ağacı, kırmızı-siyah ağaç, daire tamponu gibi gelişmiş veri yapıları. '09'da tuples, jenerik zayıf referans, eşzamanlı koleksiyon
yoktu

10

İhtiyacınız olursa arayüzü kendiniz yazmak oldukça kolaydır :

public interface ICloneable<T> : ICloneable
        where T : ICloneable<T>
{
    new T Clone();
}

Kopya haklarının şerefine bağlı olarak bir pasaj
ekledim

2
Ve bu arayüzün tam olarak nasıl çalışması gerekiyor? Bir klonlama işleminin T tipine dökülebilir olması için, klonlanan nesnenin T'den türetilmesi gerekir. Böyle bir tip kısıtlaması oluşturmanın bir yolu yoktur. Gerçekçi bir şekilde konuşursak, iCloneable geri dönüşünün sonucunu görebildiğim tek şey iCloneable türüdür.
Supercat

@supercat: evet, Clone () tam olarak bunu döndürür: ICloneable <T> uygulayan bir T. MbUnit için bu arayüzü kullanarak olmuştur yıllar yani evet, o inşaat. Uygulamaya gelince, MbUnit'in kaynağına bir göz atın.
Mauricio Scheffer

2
@Mauricio Scheffer: Neler olduğunu görüyorum. Hiçbir tip aslında iCloneable (T) 'yi uygulamamaktadır. Bunun yerine, her tür iCloneable (kendi başına) uygular. Herhalde bir dakikayı hareket ettirmek olsa da, bu işe yarar, sanırım. Bir alanı miras kısıtlaması ve arabirim alanı olarak tanımlamanın bir yolu yoktur; bir klonlama yönteminin, yarı klonlanabilir (yalnızca Korumalı yöntem olarak maruz bırakılan klonlama) baz tipinde bir nesneyi döndürmesi yararlı olabilir, ancak klonlanabilir tip kısıtlaması vardır. Bu, bir nesnenin, baz tipindeki yarı-klonlanabilir türevlerin klonlanabilir türevlerini kabul etmesine izin verecektir.
supercat

@Mauricio Scheffer: BTW, ICloneable (of T) 'nin salt okunur bir Self özelliğini (dönüş T tipi) desteklediğini öneririm. Bu, bir yöntemin ICloneable (Foo) yöntemini kabul etmesine ve (Self özelliği aracılığıyla) Foo olarak kullanmasına izin verir.
supercat

9

Son zamanlarda neden bir nesne kopyalamak yapmak korkunç bir şey? , Bu sorunun ek onaylanmaya ihtiyacı olduğunu düşünüyorum. Buradaki diğer cevaplar iyi tavsiyeler veriyor, ancak yine de cevap tam değil - neden hayır ICloneable<T>?

  1. kullanım

    Yani, onu uygulayan bir sınıfınız var. Önceden istediğiniz bir yönteminiz olsa da ICloneable, şimdi kabul etmek için genel olması gerekir ICloneable<T>. Düzenlemeniz gerekir.

    Sonra, bir nesnenin olup olmadığını kontrol eden bir yöntem olabilir is ICloneable. Şimdi ne var? Yapamazsınis ICloneable<> ve derleme türü de nesnenin türünü bilmiyorum, sen yöntem jenerik yapamazsınız. İlk gerçek sorun.

    Yani her ikisine ICloneable<T>ve ICloneableikincisine sahip olmanız gerekir . Böylece bir uygulayıcısı her iki yöntemi uygulamak gerekir - object Clone()ve T Clone(). Hayır, teşekkürler, zaten yeterince eğleniyoruz IEnumerable.

    Daha önce de belirtildiği gibi, kalıtımın karmaşıklığı da vardır. Kovaryans bu sorunu çözmüş gibi görünse de, türetilmiş bir türün ICloneable<T>kendi türünde uygulanması gerekir , ancak Clone()temel sınıfın aynı imzasına (= parametreler, temelde) sahip bir yöntem zaten vardır . Yeni klon yöntemi arayüzünüzü açık yapmak anlamsızdır, oluştururken aradığınız avantajı kaybedersiniz ICloneable<T>. newAnahtar kelimeyi ekleyin . Ancak, temel sınıfı geçersiz kılmanız gerektiğini de unutmayın Clone()(uygulama, türetilmiş tüm sınıflar için tekdüze kalmalıdır, yani aynı nesneyi her klon yönteminden döndürmek için temel klon yönteminin olması gerekir virtual)! Ama ne yazık ki, ikisini birden yapamazsınız overridevenewaynı imzalı yöntemler. İlk anahtar kelimeyi seçtiğinizde, eklerken olmasını istediğiniz hedefi kaybedersinizICloneable<T>. İkincisini seçerek, aynı dönüşü farklı nesneler yapması gereken yöntemler yaparak arayüzün kendisini kırarsınız.

  2. Nokta

    İstediğiniz ICloneable<T>Konfor için , ancak konfor arayüzlerin tasarlandığı şey değildir, anlamları (genel olarak OOP) nesnelerin davranışını birleştirmektir (C # 'da, dış davranışı birleştirmekle sınırlıdır, örneğin yöntemler ve özellikler, onların çalışmaları).

    İlk neden sizi ikna ICloneable<T>etmediyse, klon yönteminden döndürülen türü sınırlamak için kısıtlayıcı olarak da çalışabilecek itiraz edebilirsiniz. Bununla birlikte, kötü programcı ICloneable<T>T'nin onu uygulayan tip olmadığı durumlarda uygulayabilir. Bu nedenle, kısıtlamanıza ulaşmak için, genel parametreye hoş bir kısıtlama ekleyebilirsiniz:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    Kesinlikle daha kısıtlayıcı olan, whereyine de T'nin arabirimi uygulayan tür olduğunu kısıtlayamazsınız (ICloneable<T> (farklı türden uygular).

    Görüyorsunuz, bu amaca ulaşılamamıştır (orijinal ICloneablede bu konuda başarısız olur, hiçbir arabirim gerçekleyen sınıfın davranışını gerçekten sınırlayamaz).

Gördüğünüz gibi, bu genel arayüzün hem tamamen uygulanması zor hem de gerçekten gereksiz ve işe yaramaz hale geldiğini kanıtlıyor.

Ancak soruya geri dönersek, gerçekten aradığınız şey, bir nesneyi klonlarken rahatlık sağlamaktır. Bunu yapmanın iki yolu vardır:

Ek yöntemler

public class Base : ICloneable
{
    public Base Clone()
    {
        return this.CloneImpl() as Base;
    }

    object ICloneable.Clone()
    {
        return this.CloneImpl();
    }

    protected virtual object CloneImpl()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public new Derived Clone()
    {
        return this.CloneImpl() as Derived;
    }

    protected override object CloneImpl()
    {
        return new Derived();
    }
}

Bu çözüm, kullanıcılara hem konfor hem de amaçlanan davranış sağlar, ancak uygulanması çok uzun. Mevcut türü döndüren "rahat" yönteme sahip olmak istemiyorsak, sadece sahip olmak çok daha kolaydır public virtual object Clone().

Öyleyse "nihai" çözümü görelim - C # 'da bize rahatlık sağlamak için gerçekten ne amaçlanıyor?

Genişletme yöntemleri!

public class Base : ICloneable
{
    public virtual object Clone()
    {
        return new Base();
    }
}

public class Derived : Base
{
    public override object Clone()
    {
        return new Derived();
    }
}

public static T Copy<T>(this T obj) where T : class, ICloneable
{
    return obj.Clone() as T;
}

Geçerli Klon yöntemleriyle çakışmayacak şekilde Kopyala olarak adlandırılır (derleyici, türün kendi uzantı yöntemlerine göre bildirilen yöntemlerini tercih eder). Kısıt hızı için orada (vs. kontrol boş gerektirmez) 'dir.class

Umarım bu yapmamanın nedenini açıklar ICloneable<T>. Ancak, hiç uygulamanız tavsiye edilmez ICloneable.


Ek # 1: Bir jenerikin tek olası kullanımı ICloneable, Klon yönteminin boksunu atlatabileceği ve kutunun değerinin sizin elinizde olduğu anlamına gelen değer türleri içindir. Ve yapılar otomatik olarak klonlanabildiğinden (sığ), onu uygulamaya gerek yoktur (derin kopya anlamına gelmediği sürece).
IllidanS4, Monica'yı

2

Soru çok eski olmasına rağmen (bu cevapları yazdıktan 5 yıl sonra) ve zaten cevaplanmıştı, ancak bu makalenin soruyu oldukça iyi yanıtladığını buldum, buradan kontrol edin

DÜZENLE:

İşte soruyu cevaplayan makaleden alıntı (makalenin tamamını okuduğunuzdan emin olun, diğer ilginç şeyleri içerir):

İnternette Brad Abrams'ın - Microsoft'ta çalıştığı sırada - 2003'te ICloneable hakkında bazı düşüncelerin tartışıldığı bir blog yayınına işaret eden birçok referans var. Blog girişi şu adreste bulunabilir: ICloneable uygulanması . Yanıltıcı başlığa rağmen, bu blog girişi, esasen sığ / derin karışıklık nedeniyle ICloneable uygulamamayı çağırıyor. Makale doğrudan bir öneriyle bitiyor: Bir klonlama mekanizmasına ihtiyacınız varsa, kendi Klonunuzu veya Kopyalama yönteminizi tanımlayın ve derin ya da sığ bir kopya olup olmadığını açıkça belgelediğinizden emin olun. Uygun bir örüntü:public <type> Copy();


1

Büyük bir sorun, T'nin aynı sınıf olmasını kısıtlayamamalarıdır. Örneğin, bunu yapmanıza engel olan şey:

interface IClonable<T>
{
    T Clone();
}

class Dog : IClonable<JackRabbit>
{
    //not what you would expect, but possible
    JackRabbit Clone()
    {
        return new JackRabbit();
    }

}

Aşağıdaki gibi bir parametre kısıtlamasına ihtiyaç duyarlar:

interfact IClonable<T> where T : implementing_type

8
Bu kadar kötü görünmüyor class A : ICloneable { public object Clone() { return 1; } /* I can return whatever I want */ }
Nikola Novak

Sorunun ne olduğunu gerçekten göremiyorum. Hiçbir arabirim uygulamaları makul davranmaya zorlayamaz. Kendi türüyle eşleşmeyi ICloneable<T>kısıtlasa bile T, bu, bir uygulamanın Clone(), klonlandığı nesneye uzaktan benzeyen bir şeyi geri döndürmeye zorlamaz. Ayrıca, eğer bir arabirim kovaryansı kullanılıyorsa, ICloneablemühürlenmesi gereken sınıflara sahip olmak, arabirimin kendisini geri döndürmesi beklenen ICloneable<out T>bir Selfözellik içermesi en iyisi olabilir ve ...
supercat

... artığını veya sınırlamak tüketicileri var ICloneable<BaseType>ya ICloneable<ICloneable<BaseType>>. Söz BaseTypekonusu soru, protectedklonlama için, uygulayan tip tarafından çağrılacak bir yönteme sahip olmalıdır ICloneable. Bu tasarım, a Container, a CloneableContainer, a FancyContainerve a CloneableFancyContainer, ikincisinin klonlanabilir bir türevi Containergerektiren veya gerektiren bir FancyContainer(ancak klonlanabilir olup olmadığı umurumda değil) kodunda kullanılabilir olmasını isteyebilir.
supercat

Böyle bir tasarımı tercih etmemin nedeni, bir türün mantıklı bir şekilde klonlanıp kopyalanamayacağı meselesinin, diğer yönleriyle biraz dik olmasıdır. Örneğin, bir kişi FancyListmantıklı bir şekilde klonlanabilen bir türe sahip olabilir, ancak bir türev otomatik olarak durumunu bir disk dosyasında (yapıcıda belirtilen) devam ettirebilir. Türetilmiş tür klonlanamadı, çünkü durumu değiştirilebilir bir tekil dosyaya (dosyaya) eklenecekti, ancak bu, türünün çoğu özelliğine ihtiyaç duyan FancyListancak ihtiyaç duymayacak yerlerde türetilmiş türün kullanılmasını engellememelidir. klonlamak için.
supercat

+1 - bu cevap, burada gördüğüm soruya gerçekten cevap veren tek cevap.
IllidanS4, Monica'nın

0

Bu çok iyi bir soru ... Yine de kendi sorularınızı yapabilirsiniz:

interface ICloneable<T> : ICloneable
{
  new T Clone ( );
}

Andrey, kötü bir API olarak kabul edildiğini söylüyor, ancak bu arayüzün kullanımdan kaldırılmasıyla ilgili bir şey duymadım. Ve bu tonlarca arayüze zarar verir ... Klon yöntemi sığ bir kopya yapmalıdır. Nesne ayrıca derin kopya sağlarsa, aşırı yüklenmiş bir Klon (bool deep) kullanılabilir.

EDIT: bir nesneyi "klonlamak" için kullandığım desen yapıcıda bir prototip geçiriyor.

class C
{
  public C ( C prototype )
  {
    ...
  }
}

Bu, olası gereksiz kod uygulama durumlarını kaldırır. BTW, ICloneable sınırlamalarından bahsetmişken, sığ bir klon veya derin klonun, hatta kısmen sığ / kısmen derin bir klonun yapılmasına karar vermek gerçekten nesnenin kendisine bağlı değil mi? Nesne amaçlandığı gibi çalıştığı sürece gerçekten önemsemeli miyiz? Bazı durumlarda, iyi bir Klon uygulaması hem sığ hem de derin klonlamayı içerebilir.


"kötü", kullanımdan kaldırılacağı anlamına gelmez. Bu sadece "kullanmamanız daha iyi olur" anlamına gelir. "İleride ICloneable arayüzünü kaldıracağız" anlamında kullanılmıyor. Sadece kullanmamanızı teşvik ederler ve jenerikler veya diğer yeni özelliklerle güncellemezler.
jalf

Dennis: Marc'ın cevaplarına bakın. Kovaryans / kalıtım sorunları, bu arayüzü en azından marjinal olarak karmaşık olan herhangi bir senaryo için neredeyse kullanılamaz hale getirir.
Tamas Czinege

Evet, ICloneable'nin sınırlarını görebiliyorum, elbette ... Nadiren klonlamayı kullanmam gerekiyor.
baretta
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.