क्यों नहीं ICloneable <T>?


223

क्या कोई खास वजह है कि एक जेनेरिक ICloneable<T> अस्तित्व नहीं है?

यह बहुत अधिक आरामदायक होगा, अगर मुझे हर बार मुझे कुछ क्लोन करने की आवश्यकता नहीं है।


6
नहीं! सभी कारणों के साथ 'कारणों' के लिए, मैं आपसे सहमत हूं, उन्हें इसे लागू करना चाहिए था!
शिम्मी वेइटहैंडलर

Microsoft के लिए यह एक अच्छी बात होगी कि परिभाषित किया गया है (काढ़ा-आपकी अपनी इंटरफेस के साथ समस्या यह है कि दो अलग-अलग विधानसभाओं में इंटरफेस असंगत होंगे, भले ही वे शब्दार्थ समान हों)। क्या मैं इंटरफ़ेस डिजाइन कर रहा था, इसमें तीन सदस्य होंगे, क्लोन, सेल्फ, और क्लोनइम्यूटेबल, जिनमें से सभी एक T को वापस कर देंगे (अंतिम सदस्य या तो क्लोन या सेल्फ को, जैसा उचित होगा) वापस कर देगा। स्व सदस्य एक ICloneable (फू का) को एक पैरामीटर के रूप में स्वीकार करना और फिर टाइपो के लिए आवश्यकता के बिना फू के रूप में उपयोग करना संभव बना देगा।
सुपरकैट

यह एक उचित क्लोनिंग श्रेणी पदानुक्रम के लिए अनुमति देगा, जहां अंतर्निहित कक्षाएं एक संरक्षित "क्लोन" विधि को उजागर करती हैं, और एक सार्वजनिक एक को उजागर करने वाले डेरिवेटिव को सील कर दिया है। उदाहरण के लिए, किसी को पाइल, क्लोन करने योग्य हो सकता है: पाइल, एनहांसपिडाइल: पाइल, और क्लोएनेबल इनेशेडिपाइल: एन्हैंस्डपाइल, जिसमें से कोई भी नहीं तोड़ा जाएगा, यदि क्लोन किया गया हो (भले ही सभी एक सार्वजनिक क्लोनिंग विधि का पर्दाफाश करते हों), और फ़ॉरइन्हेन्स्डपाइल: एन्हैंस्डपाइल (जो टूट जाएगा) यदि क्लोन किया गया है, लेकिन किसी भी क्लोनिंग विधि को उजागर नहीं करता है)। एक रूटीन जो एक ICloneable (पाइल का) को स्वीकार करता है, एक CloneablePile या एक CloneableEnhancedPile को स्वीकार कर सकता है ...
सुपरकैट

... हालांकि CloneableEnhancedPile को CloneablePile से विरासत में नहीं मिलता है। ध्यान दें कि अगर EnhancedPile को CloneablePile से विरासत में मिला है, तो MoreEnhancedPile को एक सार्वजनिक क्लोनिंग विधि का पर्दाफाश करना होगा और कोड को पारित किया जा सकता है जो Liskov सबस्टिबिलिटी एली का उल्लंघन करते हुए, क्लोन करने की उम्मीद करेगा। चूँकि CloneableEnhancedPile ICloneable (एन्हांसपेंडाइल में से) को कार्यान्वित करेगा और निहितार्थ ICloneable (पाइल) के द्वारा, इसे Pile के क्लोन करने योग्य व्युत्पन्न की अपेक्षा एक रूटीन में पारित किया जा सकता है।
सुपरकैट

जवाबों:


111

ICloneable को एक बुरा API माना जाता है, क्योंकि यह निर्दिष्ट नहीं करता है कि परिणाम एक गहरी या उथली प्रति है। मुझे लगता है यही कारण है कि वे इस इंटरफ़ेस में सुधार नहीं करते हैं।

आप शायद एक टाइपिंग क्लोनिंग एक्सटेंशन विधि कर सकते हैं, लेकिन मुझे लगता है कि मूल विधियों की तुलना में विस्तार विधियों की प्राथमिकता कम होने के बाद इसे एक अलग नाम की आवश्यकता होगी।


8
मैं असहमत हूं, बुरा है या बुरा है, यह कुछ समय के लिए बहुत उपयोगी है, और उन मामलों में वास्तव में क्लोन <टी> की जरूरत है, ताकि अनावश्यक बॉक्सिंग और अनबॉक्सिंग को बचाया जा सके।
शिम्मी वेइटहैंडलर

2
@AndreyShchekin: गहरे बनाम उथले क्लोनिंग के बारे में क्या अस्पष्ट है? यदि List<T>कोई क्लोन विधि थी, तो मैं यह उम्मीद करूंगा कि List<T>जिनकी उपज मूल सूची में समान है, उनकी पहचान हो, लेकिन मैं उम्मीद करूंगा कि किसी भी आंतरिक डेटा संरचनाओं की नकल की जाएगी ताकि यह सुनिश्चित किया जा सके कि एक सूची में कुछ भी प्रभावित नहीं होगा। दूसरे में संग्रहीत वस्तुओं की पहचान । अस्पष्टता कहाँ है? क्लोनिंग के साथ एक बड़ी समस्या "हीरे की समस्या" की भिन्नता के साथ आती है: यदि CloneableFoo[सार्वजनिक रूप से क्लोन करने योग्य नहीं] से विरासत में मिली है Foo, तो इससे CloneableDerivedFooप्राप्त करना चाहिए ...
सुपरकैट

1
@ सुपरकैट: मैं यह नहीं कह सकता कि मैं आपकी अपेक्षा को पूरी तरह से समझता हूं, क्योंकि आप identityस्वयं सूची का क्या विचार करेंगे , उदाहरण के लिए (सूचियों की सूची के मामले में)? हालाँकि, इसे अनदेखा करते हुए, आपकी अपेक्षा एकमात्र ऐसा विचार नहीं है जो लोगों को कॉल या कार्यान्वित करते समय हो सकता है Clone। यदि कुछ अन्य सूची को लागू करने वाले पुस्तकालय लेखक आपकी अपेक्षा का पालन नहीं करते हैं तो क्या होगा? एपीआई को तुच्छ रूप से अस्पष्ट होना चाहिए, यकीनन अस्पष्ट नहीं।
एंड्री शेककिन

2
@supercat तो आप उथले प्रति के बारे में बात कर रहे हैं। हालांकि, एक गहरी प्रति के साथ मौलिक रूप से कुछ भी गलत नहीं है, उदाहरण के लिए यदि आप इन-मेमोरी ऑब्जेक्ट पर एक प्रतिवर्ती लेनदेन करना चाहते हैं। आपकी अपेक्षा आपके उपयोग के मामले पर आधारित है, अन्य लोगों की अलग-अलग अपेक्षाएं हो सकती हैं। यदि वे आपकी वस्तुओं को अपनी वस्तुओं (या इसके विपरीत) में रखते हैं, तो परिणाम अप्रत्याशित होंगे।
एंड्री शेकिन

5
@supercat आप इसे ध्यान में नहीं रखते हैं। यह .NET फ्रेमवर्क का एक हिस्सा है, आपके आवेदन का हिस्सा नहीं है। इसलिए यदि आप एक ऐसा वर्ग बनाते हैं जो गहरी क्लोनिंग नहीं करता है, तो कोई अन्य व्यक्ति एक ऐसा वर्ग बनाता है जो गहरी क्लोनिंग करता है और Cloneअपने सभी हिस्सों पर कॉल करता है , यह अनुमान से काम नहीं करेगा - यह निर्भर करता है कि वह हिस्सा आपके द्वारा लागू किया गया था या उस व्यक्ति द्वारा किसे गहरी क्लोनिंग पसंद है। पैटर्न के बारे में आपकी बात मान्य है लेकिन API में IMHO होना पर्याप्त स्पष्ट नहीं है - इसे या तो ShallowCopyबिंदु को तनाव देने के लिए कहा जाना चाहिए , या बिल्कुल भी प्रदान नहीं किया जाना चाहिए ।
एंड्री शेकिन

141

एंड्री के जवाब के अलावा (जो मैं सहमत हूं, +1) - जब किया ICloneable जाता है, तो आप सार्वजनिक कार्यान्वयन को Clone()एक टाइप किए गए ऑब्जेक्ट को वापस करने के लिए स्पष्ट कार्यान्वयन भी चुन सकते हैं :

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

बेशक एक सामान्य ICloneable<T>- वंशानुक्रम के साथ एक दूसरा मुद्दा है ।

अगर मेरे पास:

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

और मैंने लागू किया ICloneable<T>, तो क्या मैं लागू ICloneable<Foo>करूं? ICloneable<Bar>? आप जल्दी से बहुत सारे समान इंटरफेस लागू करना शुरू कर देते हैं ... एक कलाकारों की तुलना ... और क्या यह वास्तव में इतना बुरा है?


10
+1 मैंने हमेशा सोचा था कि सहसंयोजक समस्या का असली कारण था
तमस Czinege

इसके साथ एक समस्या है: स्पष्ट इंटरफ़ेस कार्यान्वयन निजी होना चाहिए , जो समस्याओं का कारण होगा कुछ बिंदु पर फू को ICloneable को कास्ट करने की आवश्यकता है ... क्या मैं यहां कुछ याद कर रहा हूं?
जोएल गो गो

3
मेरी गलती: यह पता चला है कि, निजी के रूप में परिभाषित होने के बावजूद, विधि को बुलाया जाएगा फू को IClonable (CLR के माध्यम से C # 2nd Ed, p.319) में डाल दिया जाना चाहिए। एक अजीब डिजाइन निर्णय की तरह लगता है, लेकिन यह वहाँ है। तो Foo.Clone () पहली विधि देता है, और ((ICloneable) Foo) .Clone () दूसरी विधि देता है।
जोएल में गो

दरअसल, स्पष्ट इंटरफ़ेस कार्यान्वयन, सख्ती से बोल रहे हैं, सार्वजनिक एपीआई का हिस्सा है। इसमें वे कास्टिंग के माध्यम से सार्वजनिक रूप से कॉल करने योग्य हैं।
मार्क Gravell

8
प्रस्तावित समाधान पहली बार में बहुत अच्छा लगता है ... जब तक आपको यह पता नहीं चलता है कि एक वर्ग पदानुक्रम में, आप चाहते हैं कि 'पब्लिक फू क्लोन ()' आभासी हो और व्युत्पन्न वर्गों में ओवरराइड हो ताकि वे अपने स्वयं के प्रकार (और अपने स्वयं के फ़ील्ड क्लोन कर सकें) पाठ्यक्रम)। अफसोस की बात है कि आप रिटर्न प्रकार को ओवरराइड में नहीं बता सकते हैं। इसलिए, आप फिर से मूल समस्या पर वापस आते हैं - स्पष्ट कार्यान्वयन वास्तव में आपको बहुत ज्यादा नहीं खरीदता है ('वस्तु' के बजाय अपने पदानुक्रम के आधार प्रकार को वापस करने के अलावा), और आप अपने अधिकांश को कास्टिंग करने के लिए वापस आ गए हैं क्लोन () कॉल परिणाम। नीरस
केन बेकेट

18

मुझे यह पूछने की ज़रूरत है कि इसे लागू करने के अलावा आप इंटरफ़ेस के साथ क्या करेंगे ? इंटरफेसेस आमतौर पर केवल तब उपयोगी होते हैं जब आप इसे डालते हैं (यानी यह वर्ग 'IBar' का समर्थन करता है), या इसमें पैरामीटर या सेटर हैं जो इसे लेते हैं (यानी मैं 'IBar' लेता हूं)। ICloneable के साथ - हम पूरे फ्रेमवर्क के माध्यम से गए और कहीं भी एक एकल उपयोग खोजने में विफल रहे जो इसके कार्यान्वयन के अलावा कुछ और था। हम 'वास्तविक दुनिया' में किसी भी उपयोग को खोजने में विफल रहे हैं जो इसे लागू करने के अलावा भी कुछ करता है (~ 60,000 ऐप जो हमारे पास हैं)।

अब यदि आप एक ऐसे पैटर्न को लागू करना चाहते हैं जिसे आप अपनी 'क्लोन करने योग्य' वस्तुओं को लागू करना चाहते हैं, तो यह पूरी तरह से ठीक उपयोग है - और आगे बढ़ें। आप यह भी तय कर सकते हैं कि आपके लिए "क्लोनिंग" का क्या मतलब है (यानी गहरा या उथला)। हालाँकि, उस स्थिति में, इसे परिभाषित करने के लिए हमें (BCL) की कोई आवश्यकता नहीं है। हम केवल बीसीएल में अमूर्तता को परिभाषित करते हैं जब असंबद्ध पुस्तकालयों के बीच उस अमूर्त के रूप में टाइप किए गए उदाहरणों का आदान-प्रदान करने की आवश्यकता होती है।

डेविड कीन (बीसीएल टीम)


1
गैर-जेनेरिक ICloneable बहुत उपयोगी नहीं है, लेकिन ICloneable<out T>यदि यह ISelf<out T>एक ही Selfप्रकार की विधि के साथ विरासत में मिला , तो बहुत उपयोगी हो सकता है T। किसी को अक्सर "कुछ ऐसा नहीं है जो क्लोन करने योग्य हो", लेकिन किसी को बहुत अच्छी तरह से Tक्लोन करने योग्य की आवश्यकता हो सकती है । यदि एक क्लोन करने योग्य ऑब्जेक्ट लागू होता है ISelf<itsOwnType>, तो एक रूटीन जिसे Tक्लोन करने योग्य की आवश्यकता होती है, एक प्रकार के पैरामीटर को स्वीकार कर सकता है ICloneable<T>, भले ही सभी Tसाझा पूर्वज के साझा व्युत्पन्न नहीं हो ।
सुपरकैट

धन्यवाद। यह ऊपर बताई गई कास्ट स्थिति के समान है (यानी 'इस वर्ग का समर्थन' IBar 'करता है)। अफसोस की बात है, हम केवल बहुत ही सीमित और अलग-थलग परिदृश्यों के साथ आए हैं जहां आप वास्तव में इस तथ्य का उपयोग करेंगे कि टी क्लोन है। क्या आपके मन में परिस्थितियां हैं?
डेविड कीन

एक बात जो कभी-कभी अजीब होती है। नेट एक संग्रह की सामग्री को एक मूल्य के रूप में देखा जाना चाहिए (यानी मैं बाद में उन वस्तुओं के सेट को जानना चाहूंगा जो अब संग्रह में हैं) को बदलने योग्य संग्रह का प्रबंधन कर रहा है। उसके लिए कुछ ICloneable<T>उपयोगी हो सकता है, हालांकि समानांतर उत्परिवर्तनीय और अपरिवर्तनीय वर्गों को बनाए रखने के लिए एक व्यापक रूपरेखा अधिक उपयोगी हो सकती है। दूसरे शब्दों में, कोड को यह देखने की जरूरत है कि किसी प्रकार का क्या Fooहोता है, लेकिन न तो इसे IReadableFoo
म्यूट

... कोड जो Fooएक ImmutableFooसमय कोड का उपयोग कर सकता है की सामग्री को पकड़ना चाहता है जो इसे हेरफेर करना चाहता था a का उपयोग कर सकता है MutableFoo। किसी भी प्रकार के दिए गए कोड को IReadableFooएक परस्पर या अपरिवर्तनीय संस्करण प्राप्त करने में सक्षम होना चाहिए। ऐसा ढांचा अच्छा होगा, लेकिन दुर्भाग्य से मुझे सामान्य तरीके से चीजों को स्थापित करने का कोई अच्छा तरीका नहीं मिल सकता है। यदि किसी वर्ग के लिए रीड-ओनली रैपर बनाने का एक सुसंगत तरीका था, तो इस तरह की चीज का उपयोग ICloneable<T>उस वर्ग की अपरिवर्तनीय प्रति बनाने के लिए किया जा सकता है जो धारण करता है T। '
सुपरकैट

@ सुपरकोर्ट यदि आप क्लोन करना चाहते हैं List<T>, तो ऐसा है कि क्लोन List<T>एक नया संग्रह है जो मूल संग्रह में समान वस्तुओं के सभी को इंगित करता है , इसके बिना करने के दो आसान तरीके हैं ICloneable<T>। पहला Enumerable.ToList()एक्सटेंशन तरीका है: List<foo> clone = original.ToList();दूसरा वह List<T>कंस्ट्रक्टर है जिसमें एक है IEnumerable<T>: List<foo> clone = new List<foo>(original);मुझे संदेह है कि एक्सटेंशन विधि शायद सिर्फ कंस्ट्रक्टर को बुला रही है, लेकिन ये दोनों वही करेंगे जो आप अनुरोध कर रहे हैं। ;)
CptRobby

12

मुझे लगता है कि प्रश्न "क्यों" अनावश्यक है। बहुत सारे इंटरफेस / कक्षाएं / आदि हैं ... जो बहुत उपयोगी है, लेकिन .NET फ्रेमवर्क बेस लाइब्रेरी का हिस्सा नहीं है।

लेकिन, मुख्य रूप से आप इसे स्वयं कर सकते हैं।

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(); }
}

एक अंतर्निहित वर्ग के लिए कोई भी कार्य करने योग्य क्लोन विधि, Object.Memberwise एक प्रारंभिक बिंदु के रूप में उपयोग करना चाहिए (या फिर प्रतिबिंब का उपयोग करें) क्योंकि वहाँ कोई गारंटी नहीं है कि क्लोन मूल ऑब्जेक्ट के समान प्रकार होगा। यदि आप रुचि रखते हैं तो मेरे पास एक बहुत अच्छा पैटर्न है।
सुपरकैट

@ सुपरकैट क्यों नहीं बना इसका जवाब?
नवाफ्ल

"बहुत सारे इंटरफेस / कक्षाएं / आदि हैं ... जो बहुत उपयोगी हैं, लेकिन .NET फ्रेमवर्क बेस लाइब्रेरी का हिस्सा नहीं है।" क्या आप हमें उदाहरण दे सकते हैं?
क्रिएथिक

1
@ क्राइथिक अर्थात: उन्नत वृक्ष जैसे कि बी-ट्री, रेड-ब्लैक ट्री, सर्कल बफर। '09 में कोई
तुक

10

यदि आपको इसकी आवश्यकता है तो इंटरफ़ेस को लिखना बहुत आसान है:

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

क्योंकि कॉपी-राइट के लिंक को तोड़ा गया था, उसके सम्मान में मैंने एक स्निपेट शामिल किया था ...
शिम्मी वेइटहैंडलर

2
और वास्तव में वह इंटरफ़ेस कैसे काम करने वाला है? टाइप टी के लिए एक क्लोन ऑपरेशन के परिणाम के लिए, क्लोन की जा रही वस्तु को टी से प्राप्त करना है। इस तरह के बाधा को स्थापित करने का कोई तरीका नहीं है। वास्तविक रूप से, केवल एक चीज जिसे मैं देख सकता हूं, वह है ICloneable लौटने का परिणाम एक प्रकार है iCloneable।
सुपरकैट

@ सुपरकैट: हाँ, यह वही है जो क्लोन () रिटर्न: एक टी जो ICloneable <T> को लागू करता है। MbUnit के लिए इस इंटरफ़ेस का उपयोग किया गया है साल है, तो हाँ, यह काम करता है। कार्यान्वयन के लिए, MbUnit के स्रोत पर एक नज़र डालें।
मौरिसियो शेफ़र

2
@ मौरिसियो शेफ़र: मैं देख रहा हूँ कि क्या चल रहा है। कोई प्रकार वास्तव में iCloneable (T का) लागू नहीं करता है। इसके बजाय, प्रत्येक प्रकार iCloneable (स्वयं का) लागू करता है। यह काम करेगा, मुझे लगता है, हालांकि यह सब एक टाइपकास्ट कदम है। यह बहुत बुरा है कि एक क्षेत्र को एक विरासत की कमी और एक इंटरफ़ेस के रूप में घोषित करने का कोई तरीका नहीं है; यह एक क्लोनिंग विधि के लिए एक अर्ध-क्लोन योग्य (केवल संरक्षित विधि के रूप में उजागर क्लोनिंग) बेस प्रकार की एक वस्तु वापस करने के लिए उपयोगी होगा, लेकिन एक क्लोन करने योग्य प्रकार की बाधा के साथ। यह एक आधार प्रकार के अर्ध-क्लोन योग्य डेरिवेटिव के क्लोन करने योग्य डेरिवेटिव को स्वीकार करने के लिए एक वस्तु की अनुमति देगा।
सुपरकैट

@ मौरिसियो शेफ़र: BTW, मैं सुझाव दूंगा कि ICloneable (T) भी एक स्व-स्व संपत्ति (वापसी प्रकार T का) का समर्थन करता है। यह एक विधि को एक ICloneable (फू के) को स्वीकार करने और इसे (स्व संपत्ति के माध्यम से) फू के रूप में उपयोग करने की अनुमति देगा।
सुपरकाट

9

हाल ही में लेख पढ़ने के बाद एक वस्तु की नकल करना एक भयानक काम क्यों है? , मुझे लगता है कि इस सवाल के लिए अतिरिक्त क्लिफिरिकेशन की जरूरत है। यहाँ अन्य उत्तर अच्छी सलाह प्रदान करते हैं, लेकिन फिर भी उत्तर पूर्ण नहीं है - क्यों नहीं ICloneable<T>?

  1. प्रयोग

    तो, आपके पास एक वर्ग है जो इसे लागू करता है। जबकि पहले आपके पास एक ऐसा तरीका था जो चाहता था ICloneable, अब इसे स्वीकार करने के लिए सामान्य होना चाहिए ICloneable<T>। आपको इसे संपादित करने की आवश्यकता होगी।

    फिर, आपको एक विधि मिल सकती है जो एक वस्तु की जांच करती है is ICloneable। अब क्या? आप ऐसा नहीं कर सकते हैं is ICloneable<>और जैसा कि आप संकलन-प्रकार पर ऑब्जेक्ट के प्रकार को नहीं जानते हैं, आप विधि को सामान्य नहीं बना सकते हैं। पहली वास्तविक समस्या।

    तो तुम दोनों की आवश्यकता है ICloneable<T>और ICloneable, पूर्व उत्तरार्द्ध को लागू करने। इस प्रकार एक कार्यान्वयनकर्ता को दोनों विधियों को लागू करने की आवश्यकता होगी - object Clone()और T Clone()। नहीं, धन्यवाद, हमारे पास पहले से ही पर्याप्त मज़ा है IEnumerable

    जैसा कि पहले ही बताया गया है, विरासत की जटिलता भी है। जबकि सहसंयोजक इस समस्या को हल करने के लिए लग सकता है, एक व्युत्पन्न प्रकार को लागू करने की आवश्यकता हैICloneable<T> को अपने स्वयं के प्रकार की , लेकिन पहले से ही एक ही हस्ताक्षर (= मापदंडों, मूल रूप से) के साथ एक विधि है - Clone()आधार वर्ग की। अपने नए क्लोन विधि इंटरफ़ेस को स्पष्ट बनाना व्यर्थ है, आप बनाते समय आपके द्वारा मांगे गए लाभ को खो देंगे ICloneable<T>। तो newकीवर्ड जोड़ें । लेकिन यह मत भूलो कि आपको आधार वर्ग को ओवरराइड करने की भी आवश्यकता होगी ' Clone()(कार्यान्वयन को सभी व्युत्पन्न वर्गों के लिए एक समान रहना होगा, अर्थात हर क्लोन विधि से एक ही वस्तु को वापस करने के लिए, इसलिए आधार क्लोन विधि होनी चाहिए virtual)! लेकिन, दुर्भाग्य से, आप दोनों नहीं कर सकतेoverride औरnewएक ही हस्ताक्षर के साथ तरीके। पहला कीवर्ड चुनना, आप वह लक्ष्य खो देंगे जो आप जोड़ना चाहते थे ICloneable<T>। दूसरे को चुनकर, आप इंटरफ़ेस को स्वयं ही तोड़ देंगे, ऐसी विधियाँ बना सकते हैं जो एक ही रिटर्न को अलग-अलग ऑब्जेक्ट करें।

  2. बिंदु

    आप ICloneable<T>आराम चाहते हैं , लेकिन आराम वह नहीं है जिसके लिए इंटरफेस बनाया गया है, उनका अर्थ (सामान्य ओओपी में) वस्तुओं के व्यवहार को एकजुट करने के लिए है (हालांकि सी # में, यह बाहरी व्यवहार को एकीकृत करने तक सीमित है, उदाहरण के लिए तरीके और गुण, नहीं उनके कामकाज)।

    यदि पहला कारण आपको अभी तक आश्वस्त नहीं करता है, तो आप ICloneable<T>क्लोन विधि से लौटाए गए प्रकार को सीमित करने के लिए भी प्रतिबंधित कर सकते हैं। हालांकि, गंदा प्रोग्रामर लागू कर सकता है ICloneable<T>जहां टी वह प्रकार नहीं है जो इसे लागू कर रहा है। इसलिए, अपने प्रतिबंध को प्राप्त करने के लिए, आप जेनेरिक पैरामीटर में एक अच्छा अवरोध जोड़ सकते हैं:
    public interface ICloneable<T> : ICloneable where T : ICloneable<T>
    निश्चित रूप से अधिक प्रतिबंधात्मक कि बिना एक where, आप अभी भी उस टी को प्रतिबंधित नहीं कर सकते। वह प्रकार है जो इंटरफ़ेस को लागू कर रहा है (आप ICloneable<T>विभिन्न प्रकारों से प्राप्त कर सकते हैं) इसे लागू करता है)।

    आप देखें, यहां तक ​​कि इस उद्देश्य को प्राप्त नहीं किया जा सकता है (मूल ICloneableभी इस पर विफल रहता है, कोई भी इंटरफ़ेस वास्तव में कार्यान्वयन वर्ग के व्यवहार को सीमित नहीं कर सकता है)।

जैसा कि आप देख सकते हैं, यह साबित करता है कि सामान्य इंटरफ़ेस दोनों को पूरी तरह से लागू करना कठिन है और वास्तव में अनावश्यक और बेकार है।

लेकिन सवाल पर वापस, क्या आप वास्तव में एक वस्तु की क्लोनिंग करते समय आराम चाहते हैं। इसे करने के दो तरीके हैं:

अतिरिक्त तरीके

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();
    }
}

यह समाधान उपयोगकर्ताओं को आराम और इच्छित व्यवहार प्रदान करता है, लेकिन इसे लागू करने के लिए बहुत लंबा है। यदि हम वर्तमान प्रकार को वापस करने के लिए "आरामदायक" तरीका नहीं चाहते हैं, तो यह बहुत आसान है public virtual object Clone()

तो चलो "अंतिम" समाधान देखें - क्या सी # में वास्तव में हमें आराम देने का इरादा है?

एक्सटेंशन के तरीके!

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;
}

इसका नाम कॉपी है जो वर्तमान क्लोन विधियों से नहीं टकराता है (संकलक विस्तार के प्रकारों पर स्वयं की घोषित विधियों को पसंद करता है)। classबाधा वहाँ गति के लिए (जाँच अशक्त आदि की आवश्यकता नहीं है) है।

मुझे उम्मीद है कि यह कारण स्पष्ट करता है कि क्यों नहीं बनाना है ICloneable<T>। हालांकि, इसे लागू नहीं करने की सिफारिश की गई है ICloneable


परिशिष्ट # 1: जेनेरिक ICloneableका एकमात्र संभावित उपयोग मूल्य प्रकारों के लिए है, जहां यह क्लोन विधि की बॉक्सिंग को दरकिनार कर सकता है, और इसका मतलब है कि आपके पास मूल्य अनबॉक्स है। और जैसा कि संरचनाओं को स्वचालित रूप से क्लोन किया जा सकता है (उथले रूप से), इसे लागू करने की कोई आवश्यकता नहीं है (जब तक कि आप इसे विशिष्ट नहीं बनाते हैं कि इसका मतलब गहरी प्रतिलिपि है)।
IllidanS4

2

हालाँकि यह प्रश्न बहुत पुराना है (यह उत्तर लिखने से 5 साल पहले :) और पहले से ही उत्तर दिया गया था, लेकिन मैंने पाया कि यह लेख इस प्रश्न का उत्तर काफी अच्छी तरह से देता है, यहाँ देखें

संपादित करें:

यहां लेख से उद्धरण है जो प्रश्न का उत्तर देता है (पूरा लेख पढ़ना सुनिश्चित करें, इसमें अन्य रोचक चीजें शामिल हैं):

ब्रैड अब्राम्स द्वारा 2003 के एक ब्लॉग पोस्ट की ओर इशारा करते हुए इंटरनेट पर कई संदर्भ हैं - Microsoft में नियोजित समय पर - जिसमें ICloneable के बारे में कुछ विचारों पर चर्चा की गई है। ब्लॉग प्रविष्टि इस पते पर पाई जा सकती है: कार्यान्वयन योग्य ICloneable । भ्रामक शीर्षक के बावजूद, यह ब्लॉग प्रविष्टि ICloneable को लागू नहीं करने के लिए कहता है, मुख्यतः उथले / गहरे भ्रम के कारण। अनुच्छेद एक सीधे सुझाव में समाप्त होता है: यदि आपको एक क्लोनिंग तंत्र की आवश्यकता है, तो अपने स्वयं के क्लोन, या कॉपी कार्यप्रणाली को परिभाषित करें, और सुनिश्चित करें कि आप स्पष्ट रूप से दस्तावेज करते हैं कि क्या यह एक गहरी या उथली प्रति है। एक उपयुक्त पैटर्न है:public <type> Copy();


1

एक बड़ी समस्या यह है कि वे टी को एक ही वर्ग होने के लिए प्रतिबंधित नहीं कर सकते थे। हमेशा के लिए उदाहरण आपको ऐसा करने से रोकेंगे:

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

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

}

उन्हें एक पैरामीटर प्रतिबंध की आवश्यकता है जैसे:

interfact IClonable<T> where T : implementing_type

8
यह उतना बुरा नहीं लगता class A : ICloneable { public object Clone() { return 1; } /* I can return whatever I want */ }
निकोला नोवाक

मैं वास्तव में नहीं देखता कि समस्या क्या है। कोई भी इंटरफ़ेस कार्यान्वयन को यथोचित व्यवहार करने के लिए मजबूर नहीं कर सकता है। यहां तक ​​कि अगर अपने स्वयं के प्रकार से मेल खाने के लिए ICloneable<T>विवश Tकिया जा सकता है , तो वह Clone()उस वस्तु को लागू करने के लिए किसी भी चीज को वापस करने के लिए मजबूर नहीं करेगा, जिस पर वह क्लोन किया गया था। इसके अलावा, मैं सुझाव दूंगा कि यदि कोई इंटरफ़ेस कोवरियन का उपयोग कर रहा है, तो ऐसी कक्षाएं ICloneableICloneable<out T>Self
लगवाना

... उपभोक्ताओं को ICloneable<BaseType>या करने के लिए विवश या विवश किया है ICloneable<ICloneable<BaseType>>BaseTypeसवाल में एक होना चाहिए protectedक्लोनिंग के लिए विधि है, जो प्रकार है जो औजार से कहा जा सकता है ICloneable। यह डिज़ाइन इस संभावना के लिए अनुमति देगा कि कोई एक Container, एक CloneableContainer, एक FancyContainerऔर एक की इच्छा कर सकता है , CloneableFancyContainerउत्तरार्द्ध कोड में प्रयोग करने योग्य हो सकता है जिसके लिए एक क्लोन करने योग्य व्युत्पन्न की Containerआवश्यकता होती है या जिसके लिए एक क्लोन की आवश्यकता होती है FancyContainer(लेकिन यह ध्यान देने योग्य नहीं है)।
सुपरकैट

इस तरह के डिजाइन के पक्ष में होने का कारण यह है कि इस प्रकार के मुद्दे को समझदारी से क्लोन किया जा सकता है या नहीं, इसके अन्य पहलुओं के लिए कुछ हद तक रूढ़िवादी है। उदाहरण के लिए, किसी के पास एक FancyListप्रकार हो सकता है जिसे समझदारी से क्लोन किया जा सकता है, लेकिन एक व्युत्पन्न अपने राज्य को डिस्क फ़ाइल (निर्माण में निर्दिष्ट) में स्वचालित रूप से जारी रख सकता है। व्युत्पन्न प्रकार को क्लोन नहीं किया जा सकता है, क्योंकि इसका राज्य एक उत्परिवर्तनीय सिंगलटन (फ़ाइल) से जुड़ा होगा, लेकिन इससे व्युत्पन्न प्रकार का उन स्थानों पर उपयोग नहीं किया जा सकता है जिन्हें अधिकांश विशेषताओं की FancyListआवश्यकता होगी, लेकिन इसकी आवश्यकता नहीं होगी इसे क्लोन करने के लिए।
सुपरकैट

+1 - यह उत्तर केवल उन लोगों से है जिन्होंने मुझे यहाँ देखा है जो वास्तव में प्रश्न का उत्तर देते हैं।
IllidanS4 मोनिका को

0

यह एक बहुत अच्छा सवाल है ... आप अपना खुद का बना सकते हैं, हालांकि:

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

एंड्री कहते हैं कि यह एक बुरा एपीआई माना जाता है, लेकिन मैंने इस इंटरफेस के बारे में कुछ नहीं सुना है। और इससे कई टन इंटरफेस टूट जाएगा ... क्लोन विधि को एक उथली प्रतिलिपि का प्रदर्शन करना चाहिए। यदि ऑब्जेक्ट गहरी प्रतिलिपि भी प्रदान करता है, तो एक अतिभारित क्लोन (बूल डीप) का उपयोग किया जा सकता है।

संपादित करें: पैटर्न मैं एक ऑब्जेक्ट "क्लोनिंग" के लिए उपयोग करता है, कंस्ट्रक्टर में एक प्रोटोटाइप से गुजर रहा है।

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

यह किसी भी संभावित अनावश्यक कोड कार्यान्वयन स्थितियों को हटा देता है। BTW, ICloneable की सीमाओं के बारे में बात करते हुए, क्या वास्तव में यह तय करना वस्तु पर निर्भर नहीं है कि उथले क्लोन या गहरे क्लोन, या आंशिक रूप से उथले / आंशिक रूप से गहरे क्लोन का भी प्रदर्शन किया जाना चाहिए? क्या हमें वास्तव में परवाह करनी चाहिए, जब तक कि वस्तु उद्देश्य के अनुसार काम करती है? कुछ अवसरों में, एक अच्छा क्लोन कार्यान्वयन बहुत अच्छी तरह से उथले और गहरी क्लोनिंग दोनों को शामिल कर सकता है।


"बुरा" का मतलब अपवित्र नहीं है। इसका सीधा सा मतलब है कि "आप इसका इस्तेमाल नहीं कर रहे हैं तो बेहतर है"। यह इस अर्थ में नहीं समझा जाता है कि "हम भविष्य में ICloneable इंटरफ़ेस को हटा देंगे"। वे बस आपको इसे उपयोग न करने के लिए प्रोत्साहित करते हैं, और इसे जेनरिक या अन्य नई सुविधाओं के साथ अपडेट नहीं करेंगे।
jalf

डेनिस: मार्क के जवाब देखें। सहसंयोजक / विरासत के मुद्दे इस इंटरफ़ेस को किसी भी परिदृश्य के लिए बहुत अधिक अनुपयोगी बनाते हैं जो कम से कम मामूली जटिल है।
तमस Czinege

हाँ, मैं निश्चित रूप से, ICloneable की सीमाएं देख सकता हूं ... मुझे क्लोनिंग का उपयोग करने की शायद ही कभी आवश्यकता होती है, हालांकि।
नंगे पाँव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.