programing tip

부모 클래스에서 자식 클래스로 캐스트 할 수 없습니다.

itbloger 2020. 9. 15. 07:30
반응형

부모 클래스에서 자식 클래스로 캐스트 할 수 없습니다.


부모 클래스에서 자식 클래스로 캐스트하려고하는데 InvalidCastException이 발생합니다. 자식 클래스에는 int 유형의 속성이 하나만 있습니다. 내가해야 할 일을 아는 사람이 있습니까?


포유 동물을 개에게 던질 수 없습니다. 고양이 일 수 있습니다.

음식을 샌드위치에 넣을 수는 없습니다. 치즈 버거 일 수 있습니다.

자동차를 Ferrari에 캐스팅 할 수 없습니다. Honda 일 수도 있고, 더 구체적으로 Ferrari 360 Modena를 Ferrari 360 Challange Stradale에 캐스팅 할 수 없습니다. 둘 다 Ferrari 360이지만 다른 부품이 있습니다.


C #에서 다운 캐스트하는 간단한 방법은 부모를 직렬화 한 다음 자식으로 역 직렬화하는 것입니다.

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

위의 두 줄의 코드를 사용하여 동물을 개로 캐스팅하는 간단한 콘솔 앱이 있습니다.


기본 클래스 참조가 참조하는 인스턴스는 자식 클래스의 인스턴스가 아닙니다. 잘못된 것이 없습니다.

더 구체적으로:

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

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

캐스트가 성공하려면 다운 캐스팅중인 인스턴스가 다운 캐스팅중인 클래스의 인스턴스 여야합니다 (또는 적어도 다운 캐스팅 할 클래스가 인스턴스의 클래스 계층 내에 있어야 함). 캐스트가 실패합니다.


그러한 캐스트가 의미가있는 경우가 있습니다.
제 경우에는 네트워크를 통해 BASE 클래스를 받고 있었고 더 많은 기능이 필요했습니다. 그래서 내가 원하는 모든 종소리와 휘파람으로 내 편에서 처리하도록 유도하고 수신 된 BASE 클래스를 DERIVED 클래스로 캐스팅하는 것은 단순히 옵션이 아닙니다 (Throws InvalidCastException of Course)

실용적인 생각 중 하나 는 실제로 BASE 클래스를 상속하지 않고 멤버로 포함하는 EXTENSION Helper 클래스를 선언하는 것입니다.

public class BaseExtension
{
   Base baseInstance;

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

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

당신이 느슨한 결합을 그냥하지 않고 기본 클래스에 추가 기능의 몇 가지가 필요하면 정말 유도의 절대 필요성을 가지고, 그것은 빠르고 간단한 해결 방법이 될 수 있습니다.


그것은 객체 지향 원칙에 위배됩니다. 여기와 프로젝트의 다른 곳에서 우아한 솔루션은 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);

속성은 규칙에 따라 매핑되므로 클래스가 상속 된 경우 속성 이름이 정확히 동일하고 매핑이 자동으로 구성됩니다. 구성을 조정하여 추가 속성을 추가 할 수 있습니다. 설명서를 참조하십시오 .


나는 대부분의 사람들이 명백한 부모-자녀 캐스팅 이 불가능하다고 말하는 것을 보았습니다 . 그것은 사실이 아닙니다. 수정 된 시작을 예로 들어 증명해 보겠습니다.

.net에서 알 수 있듯이 모든 주조에는 두 가지 광범위한 범주가 있습니다.

  1. 값 유형
  2. 참조 유형 (귀하의 경우 참조 유형)

참조 유형에는 시나리오가 속할 수있는 세 가지 주요 상황 사례가 추가로 있습니다.

자식에서 부모로 (암시 적 캐스팅-항상 성공)

사례 1. 직간접 부모의 자녀

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

Parent to Child (Explicit casting - Can be successful)

Case 2. Parent variable holding parent object (Not allowed)

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

Case 3. Parent variable holding child object (Always Successful)

Note: Because objects has polymorphic nature, it is possible for a variable of a parent class type to hold a child type.

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

Conclusion : After reading above all, hope it will make sense now like how parent to child conversion is possible(Case 3).

Answer To The Question :

Your answer is in case 2.Where you can see such casting is not allowed by OOP and you are trying to violate one of OOP's basic rule.So always choose safe path.

Further more, to avoid such exceptional situations .net has recommended using is/as operators those will help you to take informed decisions and provide safe casting.


Paul, you didn't ask 'Can I do it' - I am assuming you want to know how to do it!

We had to do this on a project - there are many of classes we set up in a generic fashion just once, then initialize properties specific to derived classes. I use VB so my sample is in VB (tough noogies), but I stole the VB sample from this site which also has a better C# version:

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

Sample code:

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

Of course this isn't really casting. It's creating a new derived object and copying the properties from the parent, leaving the child properties blank. That's all I needed to do and it sounds like its all you need to do. Note it only copies properties, not members (public variables) in the class (but you could extend it to do that if you are for shame exposing public members).

Casting in general creates 2 variables pointing to the same object (mini tutorial here, please don't throw corner case exceptions at me). There are significant ramifications to this (exercise to the reader)!

Of course I have to say why the languague doesn't let you go from base to derive instance, but does the other way. imagine a case where you can take an instance of a winforms textbox (derived) and store it in a variable of type Winforms control. Of course the 'control' can move the object around OK and you can deal with all the 'controll-y' things about the textbox (e.g., top, left, .text properties). The textbox specific stuff (e.g., .multiline) can't be seen without casting the 'control' type variable pointing to the textbox in memory, but it's still there in memory.

Now imagine, you have a control, and you want to case a variable of type textbox to it. The Control in memory is missing 'multiline' and other textboxy things. If you try to reference them, the control won't magically grow a multiline property! The property (look at it like a member variable here, that actually stores a value - because there is on in the textbox instance's memory) must exist. Since you are casting, remember, it has to be the same object you're pointing to. Hence it is not a language restriction, it is philosophically impossible to case in such a manner.


The instance of the object should be created using the child class's type, you can't cast a parent type instance to a child type


As of C# 7.0, you can use the is keyword to do this :

With those class defined :

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

You can then do somehting like :

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

Which would be equivalent to :

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

You could now call :

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

As well as

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

That way you can be sure that your downcast will be valid and won't throw an exception !


To cast, the actual object must be of a Type equal to or derived from the Type you are attempting to cast to...

or, to state it in the opposite way, the Type you are trying to cast it to must be the same as, or a base class of, the actual type of the object.

if your actual object is of type Baseclass, then you can't cast it to a derived class Type...


A variation on the serialization approach for those using ServiceStack:

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

or the more verbose:

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

ServiceStack's serialization might be super fast and all, but clearly, this is not a solution for massive conversions in low-latency transfers, nor for highly complex types. That's likely obvious to anyone using ServiceStack, but thought I'd clarify in anticipation of comments.


As for me it was enough to copy all property fields from the base class to the parent like this:

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

An universal solution for any object can be found here

참고URL : https://stackoverflow.com/questions/988658/unable-to-cast-from-parent-class-to-child-class

반응형