부모 클래스에서 자식 클래스로 캐스트하려고하는데 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. 직간접 부모의 자녀
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 샘플을 훔쳤습니다.
샘플 코드 :
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이 있기 때문에) 이 존재 해야 합니다. 캐스팅 중이므로 가리키는 대상과 동일한 대상이어야합니다. 따라서 그것은 언어 제한이 아니며, 그러한 방식으로 사례를 제시하는 것은 철학적으로 불가능합니다.