पेरेंट क्लास से चाइल्ड क्लास में कास्ट करने में असमर्थ


97

मैं पैरेंट क्लास से चाइल्ड क्लास में कास्ट करने की कोशिश कर रहा हूँ, लेकिन मुझे एक InvalidCastException मिलती है। बालक वर्ग के पास केवल एक प्रकार की संपत्ति होती है। क्या किसी को पता है कि मुझे क्या करने की आवश्यकता है?


यह जानना भी अच्छा है कि, आप आधार / व्युत्पन्न संबंधित वर्गों के लिए स्पष्ट कास्टिंग का उपयोग नहीं कर सकते।
रज़ासर

जवाबों:


133

सी # में डाउनकास्ट करने का एक सरल तरीका माता-पिता को क्रमबद्ध करना है और फिर इसे बच्चे में शामिल करना है।

 var serializedParent = JsonConvert.SerializeObject(parentInstance); 
 Child c  = JsonConvert.DeserializeObject<Child>(serializedParent);

मेरे पास एक सरल कंसोल ऐप है जो पशु को कुत्ते में डालती है, ऊपर की दो पंक्तियों का उपयोग करके यहां पर


19
खैर, मैं इसे "डाउनकास्ट" कहने में संकोच करूंगा।
Kirk Woll

बस एक नोट, चर नाम ऊपर समान नहीं हैं।
जेक गैस्टन

5
मुझे अच्छा लगता है जब कोई व्यक्ति बॉक्स के बाहर सोचता है और लोगों को ओपी से कहता है कि यह नहीं किया जा सकता है (एक या दो ट्रोल के लिए बचाओ)! इस पर सहायता के लिए धन्यवाद। मैं घंटे के अंतिम जोड़े के लिए यह पता लगाने की कोशिश कर रहा हूं :)
derekmx271

3
यह एक उत्कृष्ट उपाय है। मेरे पास एक ऐसा मामला था जहां मेरा बच्चा वर्ग बिना किसी अतिरिक्त कार्यक्षमता वाले माता-पिता के लिए सिर्फ एक रैपर था। मैंने ऐसा किया इसलिए मुझे अपने आवेदन में वेब संदर्भ को आयात नहीं करना पड़ा क्योंकि यह मेरी सहायक लाइब्रेरी में था। इसने मुझे माता-पिता को अपने रैपर वर्ग में बदलने की अनुमति दी। धन्यवाद!
1

1
आप एक प्रतिभाशाली हैं! :)
याबलार्गो

118

आप एक कुत्ते में एक स्तनपायी नहीं डाल सकते हैं - यह एक बिल्ली हो सकती है।

आप एक सैंडविच में भोजन नहीं डाल सकते हैं - यह एक चीज़बर्गर हो सकता है।

आप एक कार को फेरारी में नहीं डाल सकते हैं - यह एक होंडा, या अधिक विशेष रूप से हो सकता है, आप फेरारी 360 मोडेना को फेरारी 360 चैलेंज स्ट्रैडेल में नहीं डाल सकते हैं - अलग-अलग हिस्से हैं, भले ही वे दोनों फेरारी 360 हैं।


17
समझने योग्य बाधाएं, इसलिए इस तरह से वास्तव में 'कास्टिंग' की असंभवता। लेकिन क्या होगा अगर वह एक कुत्ता चाहता है जो कि आंखों के रंग / वजन / बालों के पैटर्न / उम्र, आदि के रूप में बिल्ली है जो स्तनधारी वस्तु में आयोजित किया जा रहा है? आवश्यक रूप से सामान्य गुणों की नकल करना।
फास्टअल

7
FastAl, यही कारण है कि हमारे पास इंटरफेस है। स्तनपायी को IMammal को लागू करना चाहिए और आंखों का रंग, वजन आदि शामिल करना चाहिए। अब आप IMammal में कुत्ते और बिल्ली दोनों को डाल सकते हैं।
टॉम डेलोफोर्ड

1
आप कुत्ते में स्तनपायी की कोशिश कर सकते हैं। अगर यह एक कुत्ता है, तो यह एक कुत्ता है। अन्यथा, यह अशक्त हो जाता है। "अधिभार" कार्य बिल्ली से कुत्ते के लिए असंभव रूपांतरण कर सकते हैं, अगर बिल्ली के पास ये अतिभारित कार्य हैं जो यह अनुमति देता है। लेकिन डेटा-नुकसान को संभालना और गैर-मौजूदा डेटा को अनुकूलित करना आपका काम है। जैसे पंजों को नाखूनों में
बदलना

मुझे लगता है कि उदाहरण थोड़े चरम और चयनात्मक हैं और शायद कास्ट एक कॉपी कंस्ट्रक्टर के लिए एक शॉर्टकट है। उदाहरण के लिए, बेस ऑब्जेक्ट कार में परिभाषित गुणों के साथ एक फेरारी का निर्माण। या, एक मानव के साथ शुरू करें और एक लड़का बनाएं। कास्टिंग और प्रत्यक्ष उपयोग? सहमत हूँ कि एक नहीं, नहीं है। लेकिन अगर यह कंस्ट्रक्टर का हिस्सा है या कुछ और काम कर सकता है। नीचे दिया गया क्रमिक उत्तर एक अच्छा स्पर्श है।
सिरथोमास

1
फेरारी सादृश्य एनआईसीई
लॉर्ड डार्थ वाडर

57

आपके बेस क्लास रेफरेंस का जो उदाहरण है, वह आपके चाइल्ड क्लास का उदाहरण नहीं है। कुछ गलत नहीं है।

अधिक विशेष रूप से:

Base derivedInstance = new Derived();
Base baseInstance = new Base();

Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException

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


या संभावित रूप से अन्य आधार = नया OtherDerived (); व्युत्पन्न otherFail = (Derived) otherDerived;
ब्लेयर कॉनराड

वर्ग बेस {} वर्ग व्युत्पन्न: आधार {} // मुख्य विधि में आधार व्युत्पन्न = नया व्युत्पन्न (); आधार आधार = नया आधार (); व्युत्पन्न अच्छा = (व्युत्पन्न) व्युत्पत्ति; व्युत्पन्न विफल = (व्युत्पन्न) आधार यह .NET 3.5 में किसी भी त्रुटि के बिना संकलित है। आप कहां समस्या कह रहे हैं?
प्रदीपपद

7
@pradeeptp: बेशक यह बनाता है। संकलन त्रुटि के बारे में किसने कुछ कहा?
ग्रेग डी

17

कुछ मामले हैं जब इस तरह के कलाकारों को समझ में आता है।
मेरा मामला है, मुझे नेटवर्क पर एक आधार वर्ग प्राप्त हो रहा था, और मुझे इसके लिए और अधिक विशेषताओं की आवश्यकता थी। इसलिए इसे अपनी ओर से सभी घंटियाँ और सीटी जो मैं चाहता था, को संभालने के लिए प्राप्त किया गया था, और प्राप्त आधार वर्ग को DERIVED में डालना बस एक विकल्प नहीं था (कोर्स का अमान्य अमान्य विवरण)

एक व्यावहारिक थिंक-आउट-ऑफ-द-बॉक्स सॉल्यूशन एक एक्सेलेंस हेल्पर वर्ग की घोषणा करना था जो वास्तव में बेस क्लास को विरासत में नहीं मिला था, लेकिन इसे एक सदस्य के रूप में शामिल किया गया था।

public class BaseExtension
{
   Base baseInstance;

   public FakeDerived(Base b)
   {
      baseInstance = b;
   }

   //Helper methods and extensions to Base class added here
}

आप ढीला संयोजन है और बस के बिना आधार वर्ग के लिए अतिरिक्त सुविधाओं के एक जोड़े की जरूरत है वास्तव में व्युत्पत्ति की एक निरपेक्ष की जरूरत है, कि एक त्वरित और आसान समाधान का हो सकता है।


क्या मैं यह सोचने में सही हूं कि आप चाहते हैं कि आपका BaseExtensionयहां कम से कम ऐसा लागू हो IBaseकि आप इसे इसी तरह के संदर्भों में इस्तेमाल कर सकें? या अपनी आवश्यकताओं के लिए महत्वपूर्ण नहीं था?
तोबरींद

सहित कुछ समय विरासत के लिए एक उपयुक्त प्रतिस्थापन हो सकता है
वाहिद गदिरी

17

मैंने ज्यादातर लोगों को यह कहते हुए देखा है कि बच्चे की कास्टिंग के लिए स्पष्ट माता-पिता संभव नहीं हैं, लेकिन वास्तव में यह सच नहीं है। आइए एक संशोधित शुरुआत करें और इसे उदाहरणों द्वारा साबित करने का प्रयास करें।

जैसा कि हम जानते हैं कि सभी नेटिंग में दो व्यापक श्रेणियां हैं।

  1. मान प्रकार के लिए
  2. संदर्भ प्रकार के लिए (आपके मामले में इसका संदर्भ प्रकार)

संदर्भ प्रकार में तीन मुख्य स्थितिजन्य मामले हैं जिनमें कोई भी परिदृश्य झूठ बोल सकता है।

चाइल्ड टू पैरेंट (इम्प्लिट्ट कास्टिंग - हमेशा सफल)

केस 1. किसी प्रत्यक्ष या अप्रत्यक्ष माता-पिता के लिए बच्चा

Employee e = new Employee();
Person p = (Person)e; //Allowed

बच्चे को अभिभावक (स्पष्ट कास्टिंग - सफल हो सकते हैं)

केस 2. पैरेंट वैरिएबल पैरेंट ऑब्जेक्ट को रखना (अनुमति नहीं है)

Person p = new Person();  // p is true Person object
Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue

केस 3. पैरेंट वैरिएबल चाइल्ड ऑब्जेक्ट (हमेशा सफल)

नोट: क्योंकि वस्तुओं में बहुरूपी प्रकृति होती है, इसलिए माता-पिता वर्ग प्रकार के एक चर के लिए बच्चे के प्रकार को पकड़ना संभव है।

Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed

निष्कर्ष: सभी से ऊपर पढ़ने के बाद, आशा है कि अब यह समझ में आएगा कि बाल रूपांतरण कैसे संभव है (केस 3)।

सवाल का जवाब है :

आपका उत्तर केस 2 में है। आप देख सकते हैं कि ऐसी कास्टिंग को ओओपी द्वारा अनुमति नहीं है और आप ओओपी के मूल नियम में से एक का उल्लंघन करने की कोशिश कर रहे हैं। हमेशा सुरक्षित रास्ता चुनें।

इसके अलावा, ऐसी असाधारण स्थितियों से बचने के लिए .net ने उपयोग करने की सिफारिश की है / ऑपरेटरों के रूप में वे आपको सूचित निर्णय लेने और सुरक्षित कास्टिंग प्रदान करने में मदद करेंगे।


13

यह वस्तु उन्मुख सिद्धांतों का उल्लंघन होगा। मैं कहूंगा कि एक सुरुचिपूर्ण समाधान यहाँ और परियोजना में कहीं और एक प्रक्षेपण को कॉन्फ़िगर करने के लिए AutoMapper जैसे ऑब्जेक्ट मैपिंग फ्रेमवर्क का उपयोग कर रहा है ।

यहाँ की तुलना में थोड़ा अधिक जटिल कॉन्फ़िगरेशन है, लेकिन ज्यादातर मामलों के लिए पर्याप्त लचीला है:

public class BaseToChildMappingProfile : Profile
{
    public override string ProfileName
    {
        get { return "BaseToChildMappingProfile"; }
    }

    protected override void Configure()
    {
        Mapper.CreateMap<BaseClass, ChildClassOne>();
        Mapper.CreateMap<BaseClass, ChildClassTwo>();
    }
}


public class AutoMapperConfiguration
{
    public static void Configure()
    {
        Mapper.Initialize(x =>
        {
            x.AddProfile<BaseToChildMappingProfile>();
        });
    }
}

जब एप्लिकेशन कॉल शुरू करता है AutoMapperConfiguration.Configure()और तब आप इस तरह से प्रोजेक्ट कर सकते हैं:

ChildClassOne child = Mapper.Map<BaseClass, ChildClassOne>(baseClass);

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


एक एकल संपत्ति के साथ एक प्रकार को मैप करने के लिए ऑटोमैपर का उपयोग करना (जैसे ओपी वर्णित) एक अंडे को फोड़ने के लिए एक स्लेज हथौड़ा का उपयोग करने जैसा है। क्यों न केवल व्युत्पन्न प्रकार को नया किया जाए और अपनी संपत्ति को स्वयं निर्दिष्ट करें (जो कि कोड की 1 पंक्ति है)।
बाईटदेव

9

पॉल, आपने यह नहीं पूछा 'क्या मैं यह कर सकता हूं' - मैं मान रहा हूं कि आप यह जानना चाहते हैं कि यह कैसे करना है!

हमें एक परियोजना पर ऐसा करना पड़ा - ऐसी कई कक्षाएं हैं जो हम एक बार में एक सामान्य शैली में स्थापित करते हैं, फिर व्युत्पन्न वर्गों के लिए विशिष्ट गुणों को शुरू करते हैं। मैं VB का उपयोग करता हूं, इसलिए मेरा नमूना VB (कठिन noogies) में है, लेकिन मैंने इस साइट से VB का नमूना चुरा लिया है जिसमें बेहतर C # संस्करण भी है:

http://www.eggheadcafe.com/tutorials/aspnet/a4264125-fcb0-4757-9d78-ff541dfbcb56/net-reflection--copy-cl.aspx

नमूना कोड:

Imports System
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Text
Imports System.Diagnostics

Module ClassUtils

    Public Sub CopyProperties(ByVal dst As Object, ByVal src As Object)
        Dim srcProperties() As PropertyInfo = src.GetType.GetProperties
        Dim dstType = dst.GetType

        If srcProperties Is Nothing Or dstType.GetProperties Is Nothing Then
            Return
        End If

        For Each srcProperty As PropertyInfo In srcProperties
            Dim dstProperty As PropertyInfo = dstType.GetProperty(srcProperty.Name)

            If dstProperty IsNot Nothing Then
                If dstProperty.PropertyType.IsAssignableFrom(srcProperty.PropertyType) = True Then
                    dstProperty.SetValue(dst, srcProperty.GetValue(src, Nothing), Nothing)
                End If
            End If
        Next
    End Sub
End Module


Module Module1
    Class base_class
        Dim _bval As Integer
        Public Property bval() As Integer
            Get
                Return _bval
            End Get
            Set(ByVal value As Integer)
                _bval = value
            End Set
        End Property
    End Class
    Class derived_class
        Inherits base_class
        Public _dval As Integer
        Public Property dval() As Integer
            Get
                Return _dval
            End Get
            Set(ByVal value As Integer)
                _dval = value
            End Set
        End Property
    End Class
    Sub Main()
        ' NARROWING CONVERSION TEST
        Dim b As New base_class
        b.bval = 10
        Dim d As derived_class
        'd = CType(b, derived_class) ' invalidcast exception 
        'd = DirectCast(b, derived_class) ' invalidcast exception
        'd = TryCast(b, derived_class) ' returns 'nothing' for c
        d = New derived_class
        CopyProperties(d, b)
        d.dval = 20
        Console.WriteLine(b.bval)
        Console.WriteLine(d.bval)
        Console.WriteLine(d.dval)
        Console.ReadLine()
    End Sub
End Module

बेशक यह वास्तव में कास्टिंग नहीं है। यह एक नई व्युत्पन्न वस्तु का निर्माण कर रहा है और माता-पिता से गुणों की नकल कर रहा है, जिससे बाल गुण खाली हो गया है। मुझे बस इतना ही करना है और यह आपको लगता है कि आपको करना चाहिए। ध्यान दें कि यह केवल संपत्तियों की नकल करता है, न कि सदस्यों (सार्वजनिक चर) वर्ग में (लेकिन आप इसे यह करने के लिए बढ़ा सकते हैं कि यदि आप सार्वजनिक सदस्यों को उजागर करने के लिए शर्मिंदा हैं)।

सामान्य रूप से कास्टिंग 2 चर बनाता है जो एक ही वस्तु की ओर इशारा करता है (मिनी ट्यूटोरियल यहाँ, कृपया मुझ पर कोने मामले अपवाद न फेंकें)। इस (पाठक के लिए व्यायाम) के लिए महत्वपूर्ण प्रभाव हैं!

निश्चित रूप से मुझे कहना होगा कि क्यों अस्थिर आपको आधार से उदाहरण प्राप्त करने की अनुमति नहीं देता है, लेकिन दूसरे तरीके से करता है। एक ऐसे मामले की कल्पना करें जहां आप वाइनफॉर्म टेक्स्टबॉक्स (व्युत्पन्न) का एक उदाहरण ले सकते हैं और इसे Winforms नियंत्रण के प्रकार में स्टोर कर सकते हैं। बेशक 'नियंत्रण' ऑब्जेक्ट को ओके के चारों ओर ले जा सकता है और आप टेक्स्टबॉक्स के बारे में सभी 'कंट्रोलर-वाई' चीजों से निपट सकते हैं (उदाहरण के लिए, शीर्ष, बाएं, .text गुण)। पाठ बॉक्स विशिष्ट सामग्री (जैसे, .multiline) को स्मृति में पाठ बॉक्स की ओर इशारा करते हुए 'नियंत्रण' प्रकार चर को डाले बिना नहीं देखा जा सकता है, लेकिन यह अभी भी स्मृति में है।

अब कल्पना करें, आपके पास एक नियंत्रण है, और आप इसे टाइप टेक्स्टबॉक्स के एक चर के मामले में चाहते हैं। मेमोरी में कंट्रोल 'मल्टीलाइन' और अन्य टेक्स्टबॉक्स वाली चीजें गायब हैं। यदि आप उन्हें संदर्भित करने का प्रयास करते हैं, तो नियंत्रण जादुई रूप से एक बहु-संपत्ति नहीं होगा! संपत्ति (यहां इसे एक सदस्य चर की तरह देखें, जो वास्तव में एक मूल्य संग्रहीत करता है - क्योंकि पाठ बॉक्स की मेमोरी में है) मौजूद होना चाहिए । जब से आप कास्टिंग कर रहे हैं, याद रखें, यह वही ऑब्जेक्ट होना चाहिए जो आप इंगित कर रहे हैं। इसलिए यह एक भाषा प्रतिबंध नहीं है, इस तरह से मामले में दार्शनिक रूप से असंभव है।


1
मुझे पता है कि यह इस तथ्य के बाद का तरीका है, लेकिन आपको अपनी "यदि dstProperty IsNot कुछ भी नहीं" परीक्षण में "AndAlso dstProperty.CanWrite" को शामिल करना चाहिए, तो यह सुनिश्चित करें कि इसकी आसानी से संपत्ति नहीं है।
जेम्सएमएलवी

@JamesMLV - धन्यवाद अच्छी पकड़। 'तथ्य के बाद' - ऐसा नहीं लगता कि ओपी वैसे भी किसी भी उत्तर को स्वीकार करने जा रहा है :-( इसलिए उसके बाद होने का कोई तथ्य नहीं है। ओह ठीक है।
FastAl

4

ऑब्जेक्ट का उदाहरण चाइल्ड क्लास के प्रकार का उपयोग करके बनाया जाना चाहिए, आप माता-पिता के प्रकार को बच्चे के प्रकार में नहीं डाल सकते हैं


2

मेरे लिए बेस क्लास से लेकर पैरंट्स तक सभी प्रॉपर्टी फील्ड को इस तरह कॉपी करना काफी था:

using System.Reflection;

public static ChildClass Clone(BaseClass b)
{
    ChildClass p = new ChildClass(...);

    // Getting properties of base class

    PropertyInfo[] properties = typeof(BaseClass).GetProperties();

    // Copy all properties to parent class

    foreach (PropertyInfo pi in properties)
    {
        if (pi.CanWrite)
            pi.SetValue(p, pi.GetValue(b, null), null);
    }

    return p;
}

किसी भी वस्तु के लिए एक सार्वभौमिक समाधान यहां पाया जा सकता है


2

C # 7.0 के अनुसार, आप ऐसा करने के लिए कीवर्ड का उपयोग कर सकते हैं :

परिभाषित उन वर्गों के साथ:

class Base { /* Define base class */ }
class Derived : Base { /* Define derived class */ }

आप तब कुछ कर सकते हैं जैसे:

void Funtion(Base b)
{
    if (b is Derived d)
    {
        /* Do something with d which is now a variable of type Derived */
    }
}

जो इसके बराबर होगा:

void Funtion(Base b)
{
    Defined d;
    if (b is Derived)
    {
        d = (Defined)b;
        /* Do something with d */
    }
}

अब आप कॉल कर सकते हैं:

Function(new Derived()); // Will execute code defined in if

साथ ही साथ

Function(new Base()); // Won't execute code defined in if

इस तरह से आप यह सुनिश्चित कर सकते हैं कि आपका डाउनकास्ट वैध होगा और अपवाद नहीं फेंकेगा!


1

कास्ट करने के लिए, वास्तविक ऑब्जेक्ट एक प्रकार का होना चाहिए या उस प्रकार से प्राप्त किया जाना चाहिए जिस प्रकार से आप कास्ट करने का प्रयास कर रहे हैं ...

या, इसे विपरीत तरीके से बताने के लिए, आप जिस प्रकार कास्ट करने का प्रयास कर रहे हैं, वह उसी प्रकार का होना चाहिए, या ऑब्जेक्ट का वास्तविक आधार।

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


1

ServiceStack का उपयोग करने वालों के लिए क्रमिक दृष्टिकोण पर भिन्नता:

var child = baseObject.ConvertTo<ChildType>();

या अधिक क्रिया:

var child = baseObject.ToJson().FromJson<ChildType>();

सर्विसस्टैक का क्रमांकन सुपर फास्ट और सभी हो सकता है, लेकिन स्पष्ट रूप से, यह कम-विलंबता स्थानान्तरण में बड़े पैमाने पर रूपांतरण का समाधान नहीं है, न ही अत्यधिक जटिल प्रकारों के लिए। ServiceStack का उपयोग करने वाले किसी के लिए भी यह स्पष्ट है, लेकिन मैंने टिप्पणी की प्रत्याशा में स्पष्ट किया था।

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