无法从父类转换为子类
我想从父类转换到一个子类,但我得到一个InvalidCastException。 子类只有一个inttypes的属性。 有谁知道我需要做什么?
你不能把一只哺乳动物变成一只狗 – 它可能是一只猫。
你不能把食物扔进一个三明治 – 这可能是一个芝士汉堡。
你不可能把赛车投入法拉利 – 也许是本田,或者更具体地说,你不能将法拉利360摩德纳投给法拉利360 Challange Stradale–尽pipe它们都是法拉利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
为了获得成功,您正在向下转换的实例必须是您向下转换到的类的实例(或者至less是您向下转换的类必须位于实例的类层次结构中),否则投下将失败。
有些情况下,这样的演员是有道理的。
我的情况是,我通过networking接收了一个BASE类,我需要更多的function。 所以派生它来处理它在我身边所有的钟声和口哨我想要的,并将收到的BASE类转换为DERIVED之一根本不是一个选项(抛出InvalidCastException的课程)
一个实用的解决scheme就是声明一个实际上并不inheritanceBASE类的EXTENSION Helper类,但是包含IT作为成员。
public class BaseExtension { Base baseInstance; public FakeDerived(Base b) { baseInstance = b; } //Helper methods and extensions to Base class added here }
如果你有松耦合,只需要基类的一些额外的function,而没有真正需要派生的绝对需要,这可能是一个快速和简单的解决方法。
这会违反面向对象的原则。 我会说在这里和项目的其他地方的优雅的解决scheme是使用像AutoMapper对象映射框架来configuration投影。
这是一个稍微复杂一点的configuration,但是对于大多数情况来说足够灵活:
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);
属性按照惯例映射,所以如果类inheritance,属性名称是完全相同的,映射是自动configuration的。 您可以通过调整configuration来添加其他属性。 请参阅文档 。
保罗,你没有问'我可以做' – 我假设你想知道如何做到这一点!
我们不得不在一个项目上这样做 – 有很多我们以通用方式build立的类,然后初始化特定于派生类的属性。 我使用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
当然这不是真的铸造。 它正在创build一个新的派生对象,并从父项复制这些属性,将子属性留空。 这就是我需要做的,听起来就像是你需要做的一切。 请注意,它只复制类中的属性,而不是成员(公共variables)(但是如果你羞于暴露公共成员,你可以扩展它)。
一般来说,铸造会创build两个指向同一个对象的variables(这里是迷你教程,请不要在我身上抛出exception情况)。 对此有重大影响(向读者演练)!
当然,我不得不说,为什么这个语言不会让你从基地去派生实例,而是从另一个angular度去做。 设想一种情况,你可以把一个winforms文本框(派生)的实例,并将其存储在Winforms控件types的variables中。 当然,“控制”可以移动对象,你可以处理关于文本框的所有'controll-y'的东西(例如,top,left,.text属性)。 如果没有指向内存中的文本框的“控件”typesvariables,则不能看到文本框特定的内容(例如.multiline),但它仍然存在于内存中。
现在想象一下,你有一个控件,并且你想要一个types为文本框的variables。 内存中的控制缺less'多行'和其他文本的东西。 如果你试图引用它们,那么这个控件就不会神奇地成长为多行属性! 这个属性(在这里看起来就像是一个成员variables,它实际上存储了一个值 – 因为在文本框实例的内存中) 必须存在。 既然你正在铸造,记住,它必须是你指向的同一个对象。 因此,这不是一种语言上的限制,在哲学上是不可能的。
应该使用子类的types创build对象的实例,不能将父types实例转换为子types
我看到大多数人说父母对子女的铸造是不可能的,实际上是不正确的。 我们来修改一下开始,并用实例来validation它。
正如我们在.net中所了解的,所有的铸件都有两大类。
- 对于值types
- 对于引用types(在你的情况下它的引用types)
参考types还有三个主要的情况,任何情况都可能存在。
孩子到家长(隐式铸造 – 总是成功的)
案例1.子女对任何直接或间接的父母
Employee e = new Employee(); Person p = (Person)e; //Allowed
Parent to Child(显式投射 – 可以成功)
情况2.保存父对象的父variables(不允许)
Person p = new Person(); // p is true Person object Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue
情况3.保持子对象的父variables(始终成功)
注意:由于对象具有多态性,所以父类types的variables可能包含子types。
Person p = new Employee(); // p actually is Employee Employee e = (Employee)p; // Casting allowed
结论:首先阅读之后,希望现在能够如何实现父母之间的转换(情况3)。
问题的答案:
你的回答是在案例2中 。在哪里你可以看到OOP不允许这样的转换,并且你试图违反OOP的基本规则之一。所以总是select安全的path。
此外,为了避免这种特殊情况.netbuild议使用/作为操作员,这将帮助您做出明智的决定并提供安全的铸造。
要进行强制转换, 实际的对象必须是一个types等于或者从你试图转换的types派生 出来的types。
或者以相反的方式表示它,则试图将其转换为的types必须与该对象的实际types相同或基本类。
如果您的实际对象是Baseclasstypes,那么您不能将其转换为派生类Type …
对于那些使用ServiceStack的序列化方法的变体:
var child = baseObject.ConvertTo<ChildType>();
或者更详细的:
var child = baseObject.ToJson().FromJson<ChildType>();
ServiceStack的序列化可能超级快速,但显然,这不是在低延迟传输中大规模转换的解决scheme,也不适用于高度复杂的types。 对于任何使用ServiceStack的人来说这都是显而易见的,但是我认为我会在预测意见时予以澄清。