क्या कोई खास वजह है कि एक जेनेरिक ICloneable<T>
अस्तित्व नहीं है?
यह बहुत अधिक आरामदायक होगा, अगर मुझे हर बार मुझे कुछ क्लोन करने की आवश्यकता नहीं है।
क्या कोई खास वजह है कि एक जेनेरिक ICloneable<T>
अस्तित्व नहीं है?
यह बहुत अधिक आरामदायक होगा, अगर मुझे हर बार मुझे कुछ क्लोन करने की आवश्यकता नहीं है।
जवाबों:
ICloneable को एक बुरा API माना जाता है, क्योंकि यह निर्दिष्ट नहीं करता है कि परिणाम एक गहरी या उथली प्रति है। मुझे लगता है यही कारण है कि वे इस इंटरफ़ेस में सुधार नहीं करते हैं।
आप शायद एक टाइपिंग क्लोनिंग एक्सटेंशन विधि कर सकते हैं, लेकिन मुझे लगता है कि मूल विधियों की तुलना में विस्तार विधियों की प्राथमिकता कम होने के बाद इसे एक अलग नाम की आवश्यकता होगी।
List<T>
कोई क्लोन विधि थी, तो मैं यह उम्मीद करूंगा कि List<T>
जिनकी उपज मूल सूची में समान है, उनकी पहचान हो, लेकिन मैं उम्मीद करूंगा कि किसी भी आंतरिक डेटा संरचनाओं की नकल की जाएगी ताकि यह सुनिश्चित किया जा सके कि एक सूची में कुछ भी प्रभावित नहीं होगा। दूसरे में संग्रहीत वस्तुओं की पहचान । अस्पष्टता कहाँ है? क्लोनिंग के साथ एक बड़ी समस्या "हीरे की समस्या" की भिन्नता के साथ आती है: यदि CloneableFoo
[सार्वजनिक रूप से क्लोन करने योग्य नहीं] से विरासत में मिली है Foo
, तो इससे CloneableDerivedFoo
प्राप्त करना चाहिए ...
identity
स्वयं सूची का क्या विचार करेंगे , उदाहरण के लिए (सूचियों की सूची के मामले में)? हालाँकि, इसे अनदेखा करते हुए, आपकी अपेक्षा एकमात्र ऐसा विचार नहीं है जो लोगों को कॉल या कार्यान्वित करते समय हो सकता है Clone
। यदि कुछ अन्य सूची को लागू करने वाले पुस्तकालय लेखक आपकी अपेक्षा का पालन नहीं करते हैं तो क्या होगा? एपीआई को तुच्छ रूप से अस्पष्ट होना चाहिए, यकीनन अस्पष्ट नहीं।
Clone
अपने सभी हिस्सों पर कॉल करता है , यह अनुमान से काम नहीं करेगा - यह निर्भर करता है कि वह हिस्सा आपके द्वारा लागू किया गया था या उस व्यक्ति द्वारा किसे गहरी क्लोनिंग पसंद है। पैटर्न के बारे में आपकी बात मान्य है लेकिन API में IMHO होना पर्याप्त स्पष्ट नहीं है - इसे या तो ShallowCopy
बिंदु को तनाव देने के लिए कहा जाना चाहिए , या बिल्कुल भी प्रदान नहीं किया जाना चाहिए ।
एंड्री के जवाब के अलावा (जो मैं सहमत हूं, +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>
? आप जल्दी से बहुत सारे समान इंटरफेस लागू करना शुरू कर देते हैं ... एक कलाकारों की तुलना ... और क्या यह वास्तव में इतना बुरा है?
मुझे यह पूछने की ज़रूरत है कि इसे लागू करने के अलावा आप इंटरफ़ेस के साथ क्या करेंगे ? इंटरफेसेस आमतौर पर केवल तब उपयोगी होते हैं जब आप इसे डालते हैं (यानी यह वर्ग 'IBar' का समर्थन करता है), या इसमें पैरामीटर या सेटर हैं जो इसे लेते हैं (यानी मैं 'IBar' लेता हूं)। ICloneable के साथ - हम पूरे फ्रेमवर्क के माध्यम से गए और कहीं भी एक एकल उपयोग खोजने में विफल रहे जो इसके कार्यान्वयन के अलावा कुछ और था। हम 'वास्तविक दुनिया' में किसी भी उपयोग को खोजने में विफल रहे हैं जो इसे लागू करने के अलावा भी कुछ करता है (~ 60,000 ऐप जो हमारे पास हैं)।
अब यदि आप एक ऐसे पैटर्न को लागू करना चाहते हैं जिसे आप अपनी 'क्लोन करने योग्य' वस्तुओं को लागू करना चाहते हैं, तो यह पूरी तरह से ठीक उपयोग है - और आगे बढ़ें। आप यह भी तय कर सकते हैं कि आपके लिए "क्लोनिंग" का क्या मतलब है (यानी गहरा या उथला)। हालाँकि, उस स्थिति में, इसे परिभाषित करने के लिए हमें (BCL) की कोई आवश्यकता नहीं है। हम केवल बीसीएल में अमूर्तता को परिभाषित करते हैं जब असंबद्ध पुस्तकालयों के बीच उस अमूर्त के रूप में टाइप किए गए उदाहरणों का आदान-प्रदान करने की आवश्यकता होती है।
डेविड कीन (बीसीएल टीम)
ICloneable<out T>
यदि यह ISelf<out T>
एक ही Self
प्रकार की विधि के साथ विरासत में मिला , तो बहुत उपयोगी हो सकता है T
। किसी को अक्सर "कुछ ऐसा नहीं है जो क्लोन करने योग्य हो", लेकिन किसी को बहुत अच्छी तरह से T
क्लोन करने योग्य की आवश्यकता हो सकती है । यदि एक क्लोन करने योग्य ऑब्जेक्ट लागू होता है ISelf<itsOwnType>
, तो एक रूटीन जिसे T
क्लोन करने योग्य की आवश्यकता होती है, एक प्रकार के पैरामीटर को स्वीकार कर सकता है ICloneable<T>
, भले ही सभी T
साझा पूर्वज के साझा व्युत्पन्न नहीं हो ।
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);
मुझे संदेह है कि एक्सटेंशन विधि शायद सिर्फ कंस्ट्रक्टर को बुला रही है, लेकिन ये दोनों वही करेंगे जो आप अनुरोध कर रहे हैं। ;)
मुझे लगता है कि प्रश्न "क्यों" अनावश्यक है। बहुत सारे इंटरफेस / कक्षाएं / आदि हैं ... जो बहुत उपयोगी है, लेकिन .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(); }
}
यदि आपको इसकी आवश्यकता है तो इंटरफ़ेस को लिखना बहुत आसान है:
public interface ICloneable<T> : ICloneable
where T : ICloneable<T>
{
new T Clone();
}
हाल ही में लेख पढ़ने के बाद एक वस्तु की नकल करना एक भयानक काम क्यों है? , मुझे लगता है कि इस सवाल के लिए अतिरिक्त क्लिफिरिकेशन की जरूरत है। यहाँ अन्य उत्तर अच्छी सलाह प्रदान करते हैं, लेकिन फिर भी उत्तर पूर्ण नहीं है - क्यों नहीं ICloneable<T>
?
प्रयोग
तो, आपके पास एक वर्ग है जो इसे लागू करता है। जबकि पहले आपके पास एक ऐसा तरीका था जो चाहता था 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>
। दूसरे को चुनकर, आप इंटरफ़ेस को स्वयं ही तोड़ देंगे, ऐसी विधियाँ बना सकते हैं जो एक ही रिटर्न को अलग-अलग ऑब्जेक्ट करें।
बिंदु
आप 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
।
ICloneable
का एकमात्र संभावित उपयोग मूल्य प्रकारों के लिए है, जहां यह क्लोन विधि की बॉक्सिंग को दरकिनार कर सकता है, और इसका मतलब है कि आपके पास मूल्य अनबॉक्स है। और जैसा कि संरचनाओं को स्वचालित रूप से क्लोन किया जा सकता है (उथले रूप से), इसे लागू करने की कोई आवश्यकता नहीं है (जब तक कि आप इसे विशिष्ट नहीं बनाते हैं कि इसका मतलब गहरी प्रतिलिपि है)।
हालाँकि यह प्रश्न बहुत पुराना है (यह उत्तर लिखने से 5 साल पहले :) और पहले से ही उत्तर दिया गया था, लेकिन मैंने पाया कि यह लेख इस प्रश्न का उत्तर काफी अच्छी तरह से देता है, यहाँ देखें
संपादित करें:
यहां लेख से उद्धरण है जो प्रश्न का उत्तर देता है (पूरा लेख पढ़ना सुनिश्चित करें, इसमें अन्य रोचक चीजें शामिल हैं):
ब्रैड अब्राम्स द्वारा 2003 के एक ब्लॉग पोस्ट की ओर इशारा करते हुए इंटरनेट पर कई संदर्भ हैं - Microsoft में नियोजित समय पर - जिसमें ICloneable के बारे में कुछ विचारों पर चर्चा की गई है। ब्लॉग प्रविष्टि इस पते पर पाई जा सकती है: कार्यान्वयन योग्य ICloneable । भ्रामक शीर्षक के बावजूद, यह ब्लॉग प्रविष्टि ICloneable को लागू नहीं करने के लिए कहता है, मुख्यतः उथले / गहरे भ्रम के कारण। अनुच्छेद एक सीधे सुझाव में समाप्त होता है: यदि आपको एक क्लोनिंग तंत्र की आवश्यकता है, तो अपने स्वयं के क्लोन, या कॉपी कार्यप्रणाली को परिभाषित करें, और सुनिश्चित करें कि आप स्पष्ट रूप से दस्तावेज करते हैं कि क्या यह एक गहरी या उथली प्रति है। एक उपयुक्त पैटर्न है:
public <type> Copy();
एक बड़ी समस्या यह है कि वे टी को एक ही वर्ग होने के लिए प्रतिबंधित नहीं कर सकते थे। हमेशा के लिए उदाहरण आपको ऐसा करने से रोकेंगे:
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
class A : ICloneable { public object Clone() { return 1; } /* I can return whatever I want */ }
ICloneable<T>
विवश T
किया जा सकता है , तो वह Clone()
उस वस्तु को लागू करने के लिए किसी भी चीज को वापस करने के लिए मजबूर नहीं करेगा, जिस पर वह क्लोन किया गया था। इसके अलावा, मैं सुझाव दूंगा कि यदि कोई इंटरफ़ेस कोवरियन का उपयोग कर रहा है, तो ऐसी कक्षाएं ICloneable
ICloneable<out T>
Self
ICloneable<BaseType>
या करने के लिए विवश या विवश किया है ICloneable<ICloneable<BaseType>>
। BaseType
सवाल में एक होना चाहिए protected
क्लोनिंग के लिए विधि है, जो प्रकार है जो औजार से कहा जा सकता है ICloneable
। यह डिज़ाइन इस संभावना के लिए अनुमति देगा कि कोई एक Container
, एक CloneableContainer
, एक FancyContainer
और एक की इच्छा कर सकता है , CloneableFancyContainer
उत्तरार्द्ध कोड में प्रयोग करने योग्य हो सकता है जिसके लिए एक क्लोन करने योग्य व्युत्पन्न की Container
आवश्यकता होती है या जिसके लिए एक क्लोन की आवश्यकता होती है FancyContainer
(लेकिन यह ध्यान देने योग्य नहीं है)।
FancyList
प्रकार हो सकता है जिसे समझदारी से क्लोन किया जा सकता है, लेकिन एक व्युत्पन्न अपने राज्य को डिस्क फ़ाइल (निर्माण में निर्दिष्ट) में स्वचालित रूप से जारी रख सकता है। व्युत्पन्न प्रकार को क्लोन नहीं किया जा सकता है, क्योंकि इसका राज्य एक उत्परिवर्तनीय सिंगलटन (फ़ाइल) से जुड़ा होगा, लेकिन इससे व्युत्पन्न प्रकार का उन स्थानों पर उपयोग नहीं किया जा सकता है जिन्हें अधिकांश विशेषताओं की FancyList
आवश्यकता होगी, लेकिन इसकी आवश्यकता नहीं होगी इसे क्लोन करने के लिए।
यह एक बहुत अच्छा सवाल है ... आप अपना खुद का बना सकते हैं, हालांकि:
interface ICloneable<T> : ICloneable
{
new T Clone ( );
}
एंड्री कहते हैं कि यह एक बुरा एपीआई माना जाता है, लेकिन मैंने इस इंटरफेस के बारे में कुछ नहीं सुना है। और इससे कई टन इंटरफेस टूट जाएगा ... क्लोन विधि को एक उथली प्रतिलिपि का प्रदर्शन करना चाहिए। यदि ऑब्जेक्ट गहरी प्रतिलिपि भी प्रदान करता है, तो एक अतिभारित क्लोन (बूल डीप) का उपयोग किया जा सकता है।
संपादित करें: पैटर्न मैं एक ऑब्जेक्ट "क्लोनिंग" के लिए उपयोग करता है, कंस्ट्रक्टर में एक प्रोटोटाइप से गुजर रहा है।
class C
{
public C ( C prototype )
{
...
}
}
यह किसी भी संभावित अनावश्यक कोड कार्यान्वयन स्थितियों को हटा देता है। BTW, ICloneable की सीमाओं के बारे में बात करते हुए, क्या वास्तव में यह तय करना वस्तु पर निर्भर नहीं है कि उथले क्लोन या गहरे क्लोन, या आंशिक रूप से उथले / आंशिक रूप से गहरे क्लोन का भी प्रदर्शन किया जाना चाहिए? क्या हमें वास्तव में परवाह करनी चाहिए, जब तक कि वस्तु उद्देश्य के अनुसार काम करती है? कुछ अवसरों में, एक अच्छा क्लोन कार्यान्वयन बहुत अच्छी तरह से उथले और गहरी क्लोनिंग दोनों को शामिल कर सकता है।