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

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



답변

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

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

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


답변

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

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

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


답변

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

더 구체적으로:

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

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

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


답변

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

.net에서 알다시피 모든 주물에는 두 가지 범주가 있습니다.

  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

결론 : 무엇보다 먼저 읽은 후 부모에서 자녀로의 전환이 가능한 것처럼 이제 이해가 되길 바랍니다 (Case 3).

질문에 대한 답변 :

귀하의 대답은 case 2입니다. 이러한 캐스팅이 OOP에서 허용되지 않고 OOP의 기본 규칙 중 하나를 위반하려는 경우 항상 안전한 경로를 선택하십시오.

또한 이러한 예외적 인 상황을 피하기 위해 .net은 is / as 연산자를 사용하여 정보에 입각 한 결정을 내리고 안전한 캐스팅을 제공하는 데 도움이 될 것을 권장했습니다 .


답변

그러한 캐스트가 의미가있는 경우가 있습니다.
제 경우에는 네트워크를 통해 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);

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


답변

Paul, ‘Can I do it’을 묻지 않았습니다. 방법 을 알고 싶다고 가정 합니다. 이 그것을 을 !

프로젝트에서이 작업을 수행해야했습니다. 일반적인 방식으로 한 번만 설정 한 많은 클래스가 있으며 파생 된 클래스에 고유 한 속성을 초기화합니다. VB를 사용하므로 샘플이 VB (강인한 noogies)에 있지만 더 나은 C # 버전이있는이 사이트에서 VB 샘플을 훔쳤습니다.

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 텍스트 상자 (파생)의 인스턴스를 가져와 Winforms 컨트롤 유형의 변수에 저장할 수있는 경우를 상상해보십시오. 물론 ‘control’은 개체를 OK 주변으로 이동할 수 있으며 텍스트 상자에 대한 모든 ‘controll-y’항목 (예 : top, left, .text 속성)을 처리 할 수 ​​있습니다. 텍스트 상자 특정 항목 (예 : .multiline)은 메모리의 텍스트 상자를 가리키는 ‘control’유형 변수를 캐스팅하지 않고는 볼 수 없지만 여전히 메모리에 있습니다.

이제 컨트롤이 있고 여기에 텍스트 상자 유형의 변수를 대 / 소문자로 지정하려고한다고 상상해보십시오. 메모리의 컨트롤에 ‘여러 줄’및 기타 텍스트 상자가 없습니다. 참조를 시도하면 컨트롤이 마술처럼 여러 줄 속성을 증가시키지 않습니다! 속성 (여기서는 실제로 값을 저장하는 멤버 변수처럼 보시면-텍스트 상자 인스턴스의 메모리에 on이 있기 때문에) 존재 해야 합니다. 캐스팅 중이므로 가리키는 대상과 동일한 대상이어야합니다. 따라서 그것은 언어 제한이 아니며, 그러한 방식으로 사례를 제시하는 것은 철학적으로 불가능합니다.