डाउनकास्टिंग का उचित उपयोग क्या है?


67

डाउनकास्टिंग का मतलब है बेस क्लास (या इंटरफेस) से सबक्लास या लीफ क्लास तक कास्टिंग।

यदि आप System.Objectकिसी अन्य प्रकार से डाली जाती हैं, तो डाउनकास्ट का एक उदाहरण हो सकता है ।

डाउनकास्टिंग अलोकप्रिय है, शायद एक कोड गंध: ऑब्जेक्ट ओरिएंटेड सिद्धांत को पसंद करना है, उदाहरण के लिए, डाउनकास्टिंग के बजाय आभासी या अमूर्त तरीकों को परिभाषित करना और कॉल करना।

  • क्या, यदि कोई हो, डाउनकास्टिंग के लिए अच्छे और उचित उपयोग के मामले हैं? अर्थात्, किस परिस्थिति में यह कोड लिखना उचित है?
  • यदि आपका उत्तर "कोई नहीं" है, तो डाउनकास्टिंग भाषा द्वारा समर्थित क्यों है?

5
आप जो वर्णन करते हैं उसे आमतौर पर "डाउनकास्टिंग" कहा जाता है। उस ज्ञान के साथ सशस्त्र, क्या आप पहले खुद के बारे में कुछ और शोध कर सकते हैं, और बता सकते हैं कि आपके पास मौजूद मौजूदा कारण पर्याप्त नहीं हैं? TL; DR: डाउनकास्टिंग स्टैटिक टाइपिंग को तोड़ता है, लेकिन कभी-कभी एक प्रकार की प्रणाली बहुत अधिक प्रतिबंधात्मक होती है। तो यह सुरक्षित डाउनकास्टिंग की पेशकश करने के लिए एक भाषा के लिए समझदार है।
आमोन

क्या आपका मतलब है डाउनकास्टिंग? Upcasting वर्ग पदानुक्रम के माध्यम से होगा, यानी उपवर्ग से
सुपरक्लास

मेरी क्षमा याचना: आप सही कह रहे हैं। मैंने "डाउनकास्ट" का नाम बदलकर "डाउनकास्ट" करने के लिए प्रश्न संपादित किया।
क्रिस

@amon en.wikipedia.org/wiki/Downcasting एक उदाहरण के रूप में "स्ट्रिंग में कनवर्ट" देता है (जो .Net एक आभासी का उपयोग करके समर्थन करता है ToString); इसका अन्य उदाहरण "जावा कंटेनर" है क्योंकि जावा जेनरिक (जो कि # सी करता है) का समर्थन नहीं करता है। stackoverflow.com/questions/1524197/downcast-and-upcast कहते हैं कि डाउनकास्टिंग क्या है , लेकिन जब यह उपयुक्त हो तो उदाहरण के बिना ।
क्रिस डब्ल्यूडब्ल्यू

4
@ क्रिस यह before Java had support for genericsनहीं है because Java doesn't support generics। और बहुत अधिक ने आपको जवाब दिया - जब आप जिस प्रकार की प्रणाली के साथ काम कर रहे हैं वह बहुत अधिक प्रतिबंधात्मक है और आप इसे बदलने की स्थिति में नहीं हैं।
साधारण

जवाबों:


12

यहाँ डाउनकास्टिंग के कुछ उचित उपयोग हैं।

और मैं सम्मान से जोरदार असहमत दूसरों को यहाँ जो कहते हैं कि downcasts का प्रयोग होता है के साथ निश्चित रूप से , एक कोड गंध क्योंकि मेरा मानना है कि वहाँ इसी की समस्याओं को हल करने के लिए कोई अन्य उचित तरीका है।

Equals:

class A
{
    // Nothing here, but if you're a C++ programmer who dislikes object.Equals(object),
    // pretend it's not there and we have abstract bool A.Equals(A) instead.
}
class B : A
{
    private int x;
    public override bool Equals(object other)
    {
        var casted = other as B;  // cautious downcast (dynamic_cast in C++)
        return casted != null && this.x == casted.x;
    }
}

Clone:

class A : ICloneable
{
    // Again, if you dislike ICloneable, that's not the point.
    // Just ignore that and pretend this method returns type A instead of object.
    public virtual object Clone()
    {
        return this.MemberwiseClone();  // some sane default behavior, whatever
    }
}
class B : A
{
    private int[] x;
    public override object Clone()
    {
        var copy = (B)base.Clone();  // known downcast (static_cast in C++)
        copy.x = (int[])this.x.Clone();  // oh hey, another downcast!!
        return copy;
    }
}

Stream.EndRead/Write:

class MyStream : Stream
{
    private class AsyncResult : IAsyncResult
    {
        // ...
    }
    public override int EndRead(IAsyncResult other)
    {
        return Blah((AsyncResult)other);  // another downcast (likely ~static_cast)
    }
}

यदि आप यह कहने जा रहे हैं कि ये कोड स्मेल हैं, तो आपको उनके लिए बेहतर समाधान प्रदान करने की आवश्यकता है (भले ही वे असुविधा के कारण बचा जाए)। मेरा मानना ​​है कि बेहतर समाधान मौजूद नहीं हैं।


9
"कोड गंध" का मतलब यह नहीं है "यह डिजाइन निश्चित रूप से खराब है " इसका मतलब है "यह डिजाइन संदिग्ध है "। भाषा की विशेषताएं हैं जो स्वाभाविक रूप से संदिग्ध हैं , भाषा लेखक की ओर से व्यावहारिकता का मामला है
केलथ

5
@ कैलेथ: और मेरी पूरी बात यह है कि ये डिजाइन हर समय सामने आते हैं, और यह कि मेरे द्वारा दिखाए गए डिजाइनों के बारे में कुछ भी संदिग्ध नहीं है, और इसलिए कास्टिंग कोड गंध नहीं है।
मेहरदाद

4
@ कैलेथ मैं असहमत हूं। मेरे लिए कोड गंध, का मतलब है कि कोड खराब है या, बहुत कम से कम, सुधार किया जा सकता है।
कोनराड रुडोल्फ

6
@ मेहरदाद आपकी राय से लगता है कि कोड की बदबू से एप्लिकेशन प्रोग्रामर की गलती होगी। क्यों? हर कोड गंध में एक वास्तविक समस्या नहीं है या एक समाधान है। मार्टिन फाउलर के अनुसार एक कोड गंध "एक सतह संकेत है जो आमतौर पर सिस्टम में एक गहरी समस्या से मेल खाती है" object.Equalsजो उस विवरण को काफी अच्छी तरह से फिट बैठता है (सुपरक्लास में एक समान अनुबंध प्रदान करने का प्रयास करें जो उपवर्गों को सही ढंग से लागू करने की अनुमति देता है - यह बिल्कुल सही है गैर तुच्छ)। अनुप्रयोग प्रोग्रामर के रूप में हम इसे हल नहीं कर सकते हैं (कुछ मामलों में हम इसे टाल सकते हैं) एक अलग विषय है।
Voo

5
@ मेहरदाद खैर आइए देखें कि मूल भाषा डिजाइनरों में से एक ने इक्वल्स के बारे में क्या कहा है .. Object.Equals is horrid. Equality computation in C# is completely messed up(उनके जवाब पर एरिक की टिप्पणी)। यह मज़ेदार है कि आप अपनी राय पर पुनर्विचार नहीं करना चाहते हैं, तब भी जब भाषा के प्रमुख डिज़ाइनर में से कोई एक स्वयं आपको गलत बताता है।
वू

135

डाउनकास्टिंग अलोकप्रिय है, शायद एक कोड गंध है

मैं असहमत हूं। डाउनकास्टिंग बेहद लोकप्रिय है ; वास्तविक-विश्व कार्यक्रमों की एक बड़ी संख्या में एक या अधिक डाउनकास्ट होते हैं। और यह शायद एक कोड गंध नहीं है। यह निश्चित रूप से एक कोड गंध है। इसलिए कार्यक्रम के पाठ में डाउनकास्टिंग ऑपरेशन को प्रकट करना आवश्यक है । यह इतना है कि आप गंध को आसानी से नोटिस कर सकते हैं और इस पर कोड समीक्षा पर ध्यान दे सकते हैं।

किस परिस्थिति में [कोड लिखना उचित है]

किसी भी परिस्थिति में:

  • आपके पास अभिव्यक्ति के क्रम प्रकार के बारे में एक तथ्य का 100% सही ज्ञान है जो अभिव्यक्ति के संकलन-समय प्रकार की तुलना में अधिक विशिष्ट है, और
  • संकलन-समय प्रकार पर उपलब्ध वस्तु की क्षमता का उपयोग करने के लिए आपको उस तथ्य का लाभ उठाने की आवश्यकता है, और
  • पहले दो बिंदुओं में से किसी एक को खत्म करने के लिए कार्यक्रम को रिफलेक्टर करने की तुलना में कलाकारों को लिखने के लिए समय और प्रयास का बेहतर उपयोग है।

यदि आप किसी प्रोग्राम को सस्ते में रिफ्लेक्टर कर सकते हैं ताकि या तो रनटाइम प्रकार कंपाइलर द्वारा घटाया जा सके, या प्रोग्राम को रिफलेक्टर किया जा सके ताकि आपको अधिक व्युत्पन्न प्रकार की क्षमता की आवश्यकता न हो, तो ऐसा करें। डाउनकास्ट को उन परिस्थितियों के लिए भाषा में जोड़ा गया, जहां इस तरह से कार्यक्रम को रिफ्लेक्टर करना कठिन और महंगा है।

डाउनकास्टिंग भाषा द्वारा समर्थित क्यों है?

C # का आविष्कार उन व्यावहारिक प्रोग्रामरों द्वारा किया गया था, जिनके पास जॉब करने के लिए, व्यावहारिक प्रोग्रामर के लिए जॉब है, जिनके पास जॉब करने के लिए है। C # डिज़ाइनर OO शुद्धतावादी नहीं हैं। और सी # प्रकार प्रणाली सही नहीं है; डिज़ाइन के अनुसार यह उन प्रतिबंधों को कम करता है जिन्हें एक चर के रनटाइम प्रकार पर रखा जा सकता है।

इसके अलावा, डाउनकास्टिंग C # में बहुत सुरक्षित है। हमारे पास एक मजबूत गारंटी है कि डाउनकास्ट को रनटाइम पर सत्यापित किया जाएगा और अगर यह सत्यापित नहीं किया जा सकता है तो प्रोग्राम सही काम करता है और क्रैश करता है। यह अद्भुत है; इसका अर्थ है कि यदि आपके शब्दार्थों की 100% सही समझ 99.99% सही है, तो आपका कार्यक्रम 99.99% काम करता है और बाकी समय को क्रैश कर देता है, बजाय अप्रत्याशित व्यवहार करने और उपयोगकर्ता के 0.01% समय को दूषित करने के बजाय ।


एक्सक्लूसिव: एक स्पष्ट कास्ट ऑपरेटर का उपयोग किए बिना C # में डाउनकास्ट का उत्पादन करने का कम से कम एक तरीका है । क्या आप ऐसे किसी भी परिदृश्य के बारे में सोच सकते हैं? चूँकि ये भी संभावित कोड महक होते हैं, इसलिए आपको लगता है कि डिज़ाइन के कौन से कारक हैं जो एक ऐसी विशेषता के डिज़ाइन में गए जो कोड में प्रकट कलाकारों के बिना डाउनकास्ट क्रैश का उत्पादन कर सकता है?


101
दीवारों पर हाई स्कूल में उन पोस्टरों को याद रखें "जो लोकप्रिय है वह हमेशा सही नहीं है, जो सही है वह हमेशा लोकप्रिय नहीं है?" आपको लगा कि वे आपको ड्रग्स या हाई स्कूल में गर्भवती होने से रोकने की कोशिश कर रहे हैं, लेकिन वे वास्तव में तकनीकी ऋण के खतरों के बारे में चेतावनी दे रहे थे।
corsiKa

14
@EricLippert, निश्चित रूप से फॉरच स्टेटमेंट। यह किया गया था इससे पहले कि IEnumerable सामान्य, सही था?
आर्टुरो टॉरेस सैंचेज़

18
इस स्पष्टीकरण ने मेरा दिन बना दिया। एक शिक्षक के रूप में, मुझे अक्सर पूछा जाता है कि C # इस या उस तरह से क्यों बनाया गया है, और मेरे उत्तर अक्सर आप और आपके सहयोगियों ने यहां और आपके ब्लॉग पर साझा किए गए प्रेरणाओं पर आधारित होते हैं। अनुवर्ती प्रश्न "लेकिन क्यों अय्याशी ? " का उत्तर देना अक्सर थोड़ा कठिन होता है । और यहाँ मुझे सही उत्तर मिलता है: "C # का आविष्कार व्यावहारिक प्रोग्रामर द्वारा किया गया था, जिनके पास काम करने के लिए व्यावहारिक प्रोग्रामर हैं, जिनके पास नौकरी करने के लिए है।" :-) साभार @EricLippert!
ज़ेनो

12
एक तरफ: स्पष्ट रूप से ऊपर की मेरी टिप्पणी में कहने का मतलब है कि हमारे पास ए से बी तक एक डाउनकास्ट है। मैं वास्तव में "अप" और "डाउन" और "पेरेंट" और "चाइल्ड" का उपयोग नापसंद करता हूं जब एक क्लास पदानुक्रम को नेविगेट करता है और मैं उन्हें प्राप्त करता हूं मेरे लेखन में हर समय गलत। प्रकारों के बीच संबंध एक सुपरसेट / सबसेट संबंध है, इसलिए इसके साथ जुड़े या नीचे एक दिशा क्यों होनी चाहिए, मुझे नहीं पता। और मुझे "माता-पिता" पर शुरू भी मत करो। जिराफ के माता-पिता स्तनपायी नहीं हैं, जिराफ के माता-पिता श्री और श्रीमती जिराफ हैं।
एरिक लिपर्ट

9
Object.Equalsहै भयंकर । C # में समानता की गणना पूरी तरह से गड़बड़ है , अब तक एक टिप्पणी में समझाने के लिए गड़बड़ की गई है। समानता का प्रतिनिधित्व करने के लिए बहुत सारे तरीके हैं; उन्हें असंगत बनाना बहुत आसान है; यह वास्तव में एक समानता ऑपरेटर (समरूपता, संवेदनशीलता और पारगम्यता) के लिए आवश्यक गुणों का उल्लंघन करने के लिए आवश्यक है। क्या मैं स्क्रैच से एक प्रकार की प्रणाली डिजाइन कर रहा था आज वहाँ बिल्कुल नहीं Equals( GetHashcodeया ToString) होगा Object
एरिक लिपर्ट

33

इवेंट हैंडलर्स में आमतौर पर हस्ताक्षर होते हैं MethodName(object sender, EventArgs e)। कुछ मामलों में यह संभव है कि किस प्रकार के संबंध में घटना को संभालना संभव senderहै, या यहां तक ​​कि बिना उपयोग किए भी sender, लेकिन दूसरों senderको घटना को संभालने के लिए अधिक विशिष्ट प्रकार के लिए डाला जाना चाहिए।

उदाहरण के लिए, आपके पास दो TextBoxs हो सकते हैं और आप उनमें से प्रत्येक से एक घटना को संभालने के लिए एक एकल प्रतिनिधि का उपयोग करना चाहते हैं। फिर आपको इस पर कास्ट senderकरने की आवश्यकता होगी TextBoxताकि आप ईवेंट को संभालने के लिए आवश्यक गुणों तक पहुंच सकें:

private void TextBox_TextChanged(object sender, EventArgs e)
{
   TextBox box = (TextBox)sender;
   box.BackColor = string.IsNullOrEmpty(box.Text) ? Color.Red : Color.White;
}

हां, इस उदाहरण में, आप अपना खुद का उपवर्ग बना सकते हैं, TextBoxजिसने आंतरिक रूप से ऐसा किया है। हमेशा व्यावहारिक या संभव नहीं, हालांकि।


2
धन्यवाद, यह एक अच्छा उदाहरण है। TextChangedघटना के एक सदस्य है Control- मुझे आश्चर्य है कि क्यों senderकी टाइप नहीं है Controlलिखने के बजाय object? इसके अलावा, मुझे आश्चर्य है कि क्या (सैद्धांतिक रूप से) रूपरेखा प्रत्येक उपवर्ग के लिए घटना को पुनर्परिभाषित कर सकती है (ताकि उदाहरण के लिए प्रेषक के प्रकार का TextBoxउपयोग करके घटना का एक नया संस्करण TextBoxहो सकता है) ... जो डाउनकोडिंग की आवश्यकता हो सकती है (या नहीं) करने के लिए TextBox) के अंदर TextBoxकार्यान्वयन (नई घटना प्रकार लागू करने के लिए), लेकिन आवेदन कोड के ईवेंट हैंडलर में एक खिन्न की आवश्यकता होती है से बच जाएंगे।
क्रिस डब्ल्यूडब्ल्यू

3
यह स्वीकार्य माना जाता है क्योंकि ईवेंट हैंडलिंग को सामान्य बनाना मुश्किल है यदि प्रत्येक ईवेंट का अपना स्वयं का हस्ताक्षर है। हालांकि मुझे लगता है कि यह एक स्वीकृत सीमा है बजाय इसके कि सबसे अच्छा अभ्यास माना जाता है क्योंकि विकल्प वास्तव में अधिक परेशानी वाला है। यदि कोड को जटिल किए बिना TextBox senderइसके बजाय शुरू करना संभव था object sender, तो मुझे यकीन है कि यह किया जाएगा।
नील

6
@Neil ईवेंटिंग सिस्टम विशेष रूप से मनमाने ऑब्जेक्ट्स के लिए मनमाने डेटा विखंडनों को अग्रेषित करने की अनुमति देने के लिए बनाए गए हैं। प्रकार की शुद्धता के लिए रनटाइम-चेक के बिना ऐसा करना असंभव है।
जोकर_vD

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

@KonradRudolph ज़रूर, लेकिन आप ईवेंट कतार में मनमाने प्रकार की घटनाओं को कैसे संग्रहीत करेंगे? क्या आप केवल कतार से छुटकारा पाने और प्रत्यक्ष प्रेषण के लिए जाते हैं?
जोकर_vD

17

जब किसी चीज से आपको सुपरपाइप मिलती है तो आपको डाउनकास्टिंग की जरूरत होती है और आपको उपप्रकार के आधार पर इसे अलग तरह से संभालने की जरूरत होती है।

decimal value;
Donation d = user.getDonation();
if (d is CashDonation) {
     value = ((CashDonation)d).getValue();
}
if (d is ItemDonation) {
     value = itemValueEstimator.estimateValueOf(((ItemDonation)d).getItem());
}
System.out.println("Thank you for your generous donation of $" + value);

नहीं, इससे अच्छी गंध नहीं आती है। एक आदर्श दुनिया Donationमें एक अमूर्त विधि होती getValueऔर प्रत्येक कार्यान्वयन उपप्रकार में एक उपयुक्त कार्यान्वयन होता।

लेकिन क्या होगा अगर ये कक्षाएं एक पुस्तकालय से हैं जिन्हें आप बदल नहीं सकते हैं या नहीं करना चाहते हैं। फिर कोई दूसरा विकल्प नहीं है।


यह विज़िटर पैटर्न का उपयोग करने के लिए एक अच्छा समय लगता है। या जो dynamicकीवर्ड समान परिणाम प्राप्त करता है।
user2023861

3
@ user2023861 मुझे नहीं लगता कि विज़िटर पैटर्न डोनेशन उपवर्गों के सहयोग पर निर्भर करता है - यानी डोनेशन को एक सार घोषित करने की आवश्यकता होती है void accept(IDonationVisitor visitor), जिसे इसके उपवर्ग विशिष्ट (जैसे अतिभारित) तरीकों को कॉल करके लागू करते हैं IDonationVisitor
क्रिस 14

@ क्रिस, तुम उस बारे में सही हो। सहयोग के बिना, आप अभी भी dynamicकीवर्ड का उपयोग करके कर सकते हैं।
user2023861

इस विशेष मामले में आप दान के लिए एक अनुमान विधि जोड़ सकते हैं।
इमिबिज़

@ user2023861: dynamicअभी भी डाउनकास्टिंग, बस धीमी है।
मेहरदाद

12

मैं टिप्पणी नहीं कर सकता क्योंकि एरिक लिपर्ट के जवाब में जोड़ने के लिए ...

आप अक्सर जेनेरिक का उपयोग करने के लिए एपीआई को रीफ्रैक्ट करके डाउनकास्टिंग से बच सकते हैं। लेकिन संस्करण 2.0 तक भाषा में जेनेरिक नहीं जोड़े गए थे । यहां तक ​​कि अगर आपके अपने कोड को प्राचीन भाषा संस्करणों का समर्थन करने की आवश्यकता नहीं है, तो आप अपने आप को उन विरासत वर्गों का उपयोग कर सकते हैं जो कि मानकीकृत नहीं हैं। उदाहरण के लिए, कुछ वर्ग को एक प्रकार का पेलोड ले जाने के लिए परिभाषित किया जा सकता है Object, जिसे आप उस प्रकार के लिए डालने के लिए मजबूर हैं जिसे आप जानते हैं कि यह वास्तव में है।


वास्तव में, कोई यह कह सकता है कि जावा या सी # में जेनरिक अनिवार्य रूप से सुपरक्लास ऑब्जेक्ट्स को संचय करने के लिए एक सुरक्षित आवरण है और तत्वों का फिर से उपयोग करने से पहले सही (कंपाइलर-चेक किए गए) उप-वर्ग के लिए डाउनकास्टिंग करते हैं। (C ++ टेम्प्लेट के विपरीत, जो कोड को हमेशा उपवर्ग का उपयोग करने के लिए फिर से लिखता है और इस प्रकार डाउनकास्ट करने से बचता है - जो मेमोरी प्रदर्शन को बढ़ावा दे सकता है, यदि अक्सर संकलन समय और निष्पादन योग्य आकार की कीमत पर।)
वामावर्त

4

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

वहाँ "असुरक्षित" निर्माणों की मदद से आप कर रहे हैं अप्रमाणित संकलक के दावे। उदाहरण के लिए, बिना शर्त कॉल Nullable.Value, बिना शर्त डाउनकास्ट, dynamicऑब्जेक्ट्स, आदि। वे आपको दावा करने की अनुमति देते हैं ("मैं दावा करता हूं कि वस्तु aएक है String, अगर मैं गलत हूं, तो इसे साबित करने की आवश्यकता के बिनाInvalidCastException " फेंक दें ) । यह उन मामलों में उपयोगी हो सकता है जहां यह साबित करना सार्थक की तुलना में काफी कठिन है।

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


मुझे लगता है कि मैं पूछ रहा हूँ, तो, इस तरह के "अप्रमाणित दावे" बनाने के लिए कुछ उदाहरणों के लिए [एस] जहां / जब यह आवश्यक या वांछनीय (यानी अच्छा अभ्यास) है।
क्रिस डब्ल्यूडब्ल्यू

1
प्रतिबिंब संचालन के परिणाम के बारे में कैसे बताएं? stackoverflow.com/a/3255716/3141234
अलेक्जेंडर

3

डाउनकास्टिंग को कुछ कारणों से बुरा माना जाता है। मुख्य रूप से मुझे लगता है कि यह ओओपी विरोधी है।

OOP वास्तव में इसे पसंद करेगा यदि आपको कभी भी इसके '' raison-d'etre '' के रूप में डाउनकास्ट नहीं करना पड़ा है, तो यह है कि बहुरूपता का अर्थ है कि आपको जाना नहीं है

if(object is X)
{
    //do the thing we do with X's
}
else
{
    //of the thing we do with Y's
}

तुम बस करो

x.DoThing()

और कोड ऑटो-जादुई रूप से सही काम करता है।

नीचे नहीं करने के लिए कुछ 'कठिन' कारण हैं:

  • C ++ में यह धीमा है।
  • यदि आप गलत प्रकार चुनते हैं तो आपको रनटाइम त्रुटि मिलती है।

लेकिन कुछ परिदृश्यों में डाउनकास्टिंग का विकल्प बहुत बदसूरत हो सकता है।

क्लासिक उदाहरण संदेश प्रसंस्करण है, जहां आप ऑब्जेक्ट में प्रोसेसिंग फ़ंक्शन नहीं जोड़ना चाहते हैं, लेकिन इसे संदेश प्रोसेसर में रखें। फिर मेरे पास MessageBaseClassप्रक्रिया के लिए एक सरणी में एक टन है , लेकिन मुझे सही प्रसंस्करण के लिए उपप्रकार द्वारा प्रत्येक की आवश्यकता है।

आप डबल डिस्पैच का उपयोग समस्या (चारों ओर पाने के लिए कर सकते हैं https://en.wikipedia.org/wiki/Double_dispatch ) ... लेकिन वह भी अपनी समस्याएं हैं। या आप बस उन कठिन कारणों के जोखिम पर कुछ सरल कोड के लिए डाउनकास्ट कर सकते हैं।

लेकिन जेनरिक का आविष्कार होने से पहले यह था। अब आप एक प्रकार प्रदान करके डाउनकास्टिंग से बच सकते हैं जहां बाद में विवरण निर्दिष्ट किया गया है।

MessageProccesor<T> इसके प्रकार के मापदंडों को अलग-अलग कर सकते हैं और निर्दिष्ट प्रकार से मान लौटा सकते हैं लेकिन फिर भी सामान्य कार्यक्षमता प्रदान करते हैं

बेशक कोई भी आपको ओओपी कोड लिखने के लिए मजबूर नहीं कर रहा है और ऐसी बहुत सारी भाषा सुविधाएँ हैं जो प्रदान की जाती हैं लेकिन प्रतिबिंब पर ऐसी होती हैं।


1
मुझे संदेह है कि यह C ++ में धीमा है, खासकर यदि आप static_castइसके बजाय dynamic_cast
क्रिस डब्ल्यूडब्ल्यू

"संदेश-प्रसंस्करण" - मुझे लगता है कि इसका अर्थ है कि उदाहरण के lParamलिए कुछ विशिष्ट करने के लिए कास्टिंग । मुझे यकीन नहीं है कि यह एक अच्छा C # उदाहरण है।
क्रिस डब्ल्यूडब्ल्यू

3
@ क्रिस - ठीक है, हाँ, static_castहमेशा तेज़ रहता है ... लेकिन अगर आपको कोई गलती नहीं करनी है और अपरिहार्य तरीके से विफल होने की गारंटी नहीं है, तो वह नहीं है जो आप उम्मीद कर रहे हैं।
जूल्स

@ जूल्स: यह पूरी तरह से बिंदु के पास है।
मेहरदाद

@ मेहरदाद, बिल्कुल यही बात। C ++ में बराबर c # कास्ट करना धीमा है। और यह कास्टिंग के खिलाफ पारंपरिक कारण है। वैकल्पिक static_cast समतुल्य नहीं है और अपरिभाषित व्यवहार के कारण बुरा विकल्प माना जाता है
Ewan

0

पहले सभी के अलावा, एक टैग संपत्ति की कल्पना करें जो कि प्रकार की वस्तु है। यह आपको बाद में उपयोग के लिए अपनी पसंद की वस्तु को किसी अन्य वस्तु में संग्रहीत करने का एक तरीका प्रदान करता है। आपको इस संपत्ति को डाउनकास्ट करने की आवश्यकता है।

सामान्य तौर पर, आज तक कुछ का उपयोग नहीं करना हमेशा कुछ बेकार का संकेत नहीं है ;-)


हां उदाहरण के लिए देखें .net में टैग प्रॉपर्टी का क्या उपयोग है । वहां के किसी एक उत्तर में, किसी ने लिखा था, मैंने इसका उपयोग उपयोगकर्ता के लिए विंडोज प्रपत्र अनुप्रयोगों में इनपुट निर्देशों के लिए किया था। जब नियंत्रण GotFocus घटना शुरू हो गया, निर्देश लेबल। कस्टम संपत्ति मेरे नियंत्रण टैग संपत्ति का मूल्य सौंपा गया था जिसमें अनुदेश स्ट्रिंग शामिल था। मुझे लगता है कि Control( object Tagसंपत्ति का उपयोग करने के बजाय ) 'विस्तार' का एक वैकल्पिक तरीका है कि Dictionary<Control, string>प्रत्येक नियंत्रण के लिए लेबल को स्टोर करने के लिए एक निर्माण करना होगा ।
क्रिसडब्ल्यू

1
मुझे यह उत्तर पसंद है क्योंकि Tagएक .Net फ्रेमवर्क वर्ग की सार्वजनिक संपत्ति है, जो यह बताती है कि आप (अधिक-या-कम) इसका उपयोग करने वाले हैं।
क्रिस डब्ल्यूडब्ल्यू

@ChrisW: नहीं कहने के लिए निष्कर्ष गलत है (या सही) है, लेकिन ध्यान दें कि कि लापरवाह तर्क है, सार्वजनिक नेट कार्यक्षमता के बहुत सारे है कि क्योंकि वहाँ है (जैसे कि, अप्रचलित System.Collections.ArrayList) या अन्यथा पदावनत (जैसे कि, IEnumerator.Reset)।
मेहरदाद

0

मेरे काम में डाउनकास्टिंग का सबसे आम उपयोग लिस्कोव के प्रतिस्थापन सिद्धांत को तोड़ने वाले कुछ बेवकूफों के कारण है।

कल्पना कीजिए कि कुछ तीसरे पक्ष के परिवाद में एक इंटरफ़ेस है।

public interface Foo
{
     void SayHello();
     void SayGoodbye();
 }

और 2 कक्षाएं जो इसे लागू करती हैं। Barऔर QuxBarअच्छा व्यवहार किया जाता है।

public class Bar
{
    void SayHello()
    {
        Console.WriteLine(“Bar says hello.”);
    }
    void SayGoodbye()
    {
         Console.WriteLine(“Bar says goodbye”);
    }
}

लेकिन Quxअच्छी तरह से व्यवहार नहीं किया है।

public class Qux
{
    void SayHello()
    {
        Console.WriteLine(“Qux says hello.”);
    }
    void SayGoodbye()
    {
        throw new NotImplementedException();
    }
}

अच्छा ... अब मेरे पास कोई विकल्प नहीं है। मैं है क्रम में जांच और (संभवतः) खिन्न टाइप करने के लिए मेरा कार्यक्रम एक क्रैश होने थम होने से बचाने के।


1
इस विशेष उदाहरण के लिए सही नहीं है। जब आप SayGoodbye को कॉल कर रहे हैं तो आप NotImplementedException को पकड़ सकते हैं।
प्रति वॉन ज़्वीबर्गबर्ग

यह शायद एक बहुत ही सरल उदाहरण है, लेकिन कुछ पुराने .Net API के साथ काम करें और आप इस स्थिति में चले जाएंगे। कभी-कभी कोड लिखने का सबसे आसान तरीका सुरक्षित कास्ट अला है var qux = foo as Qux;
रबरडुक

0

एक और वास्तविक दुनिया का उदाहरण WPF है VisualTreeHelper। यह खिन्न का उपयोग करता है कास्ट करने के लिए DependencyObjectकरने के लिए Visualऔर Visual3D। उदाहरण के लिए, VisualTreeHelper.GetParent । खिन्न में होता है VisualTreeUtils.AsVisualHelper :

private static bool AsVisualHelper(DependencyObject element, out Visual visual, out Visual3D visual3D)
{
    Visual elementAsVisual = element as Visual;

    if (elementAsVisual != null)
    {
        visual = elementAsVisual;
        visual3D = null;
        return true;
    }

    Visual3D elementAsVisual3D = element as Visual3D;

    if (elementAsVisual3D != null)
    {
        visual = null;
        visual3D = elementAsVisual3D;
        return true;
    }            

    visual = null;
    visual3D = null;
    return false;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.