我正在尝试从父类转换为子类,但是我得到了InvalidCastException。子类只有一个int类型的属性。有谁知道我需要做什么?
答案 0 :(得分:104)
你不能将哺乳动物变成狗 - 它可能是一只猫。
你不能把食物撒成三明治 - 它可能是芝士汉堡。
你不能将汽车投入法拉利 - 它可能是本田,或者更具体地说,你不能将法拉利360摩德纳投射到法拉利360 Challange Stradale - 虽然它们都是两者,但它们有不同的部分法拉利360s。
答案 1 :(得分:102)
在C#中向下转换的一种简单方法是序列化父级,然后将其反序列化为子级。
var serializedParent = JsonConvert.SerializeObject(parentInstance);
Child c = JsonConvert.DeserializeObject<Child>(serializedParent);
我有一个简单的控制台应用程序,使用上面两行代码here
将动物投入狗答案 2 :(得分:56)
您的基类引用所引用的实例不是您的子类的实例。没有错。
更具体地说:
Base derivedInstance = new Derived();
Base baseInstance = new Base();
Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException
为了使转换成功,您正在向下转换的实例必须是您正在向下转换的类的实例(或者至少,您要向下转换的类必须在实例的类层次结构中)否则演员会失败。
答案 3 :(得分:17)
在某些情况下,这样的演员会有意义 我的情况是,我通过网络收到了一个BASE课程,我需要更多的功能。 因此得出它来处理它我所需要的所有花里胡哨,并将接收到的BASE类转换为DERIVED一个根本不是一个选项(当然抛出InvalidCastException)
一个实用的开箱即用的解决方案是声明一个EXTENSION Helper类,它实际上并没有继承BASE类,而是包含它作为成员。
public class BaseExtension
{
Base baseInstance;
public FakeDerived(Base b)
{
baseInstance = b;
}
//Helper methods and extensions to Base class added here
}
如果你有松散的耦合,只需要一些基本类的额外功能,而真的绝对需要推导,那么这可能是一个快速而简单的解决方法。
答案 4 :(得分: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);
属性按约定映射,因此如果继承该类,则属性名称完全相同,并自动配置映射。您可以通过调整配置来添加其他属性。请参阅documentation。
答案 5 :(得分:10)
我们必须在一个项目上执行此操作 - 我们只以通用方式设置了许多类,然后初始化特定于派生类的属性。我使用VB所以我的样本是在VB(艰难的noogies),但我偷了这个网站的VB样本,它也有一个更好的C#版本:
示例代码:
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
当然这不是真的。它正在创建一个新的派生对象并从父项复制属性,将子属性保留为空。这就是我需要做的一切,听起来就像你需要做的一切。请注意,它只复制属性,而不是类中的成员(公共变量)(但是如果你因为暴露公共成员而感到羞耻,你可以扩展它)。
一般来说,Casting会创建两个指向同一个对象的变量(这里的迷你教程,请不要向我抛出异常案例例外)。这有很大的后果(向读者练习)!
当然,我必须说明为什么语言不会让你从基地出发来推导实例,而是以另一种方式。想象一下你可以获取winforms文本框(派生)的实例并将其存储在Winforms控件类型的变量中的情况。当然,'控件'可以将对象移动到OK,你可以处理关于文本框的所有'controll-y'事物(例如,top,left,.text属性)。如果没有将指向内存中文本框的'control'类型变量强制转换,则无法看到特定于文本框的内容(例如.multiline),但它仍然存在于内存中。
现在想象一下,你有一个控件,并且你想要一个类型为textbox的变量。记忆中的控制缺少“多线”和其他文字的东西。如果您尝试引用它们,控件将不会神奇地增长多行属性!属性(在这里看它就像一个成员变量,实际上存储一个值 - 因为文本框实例的内存中存在)必须存在。因为你正在施法,记住,它必须是你指向的同一个物体。因此,它不是一种语言限制,在这种情况下在哲学上是不可能的。
答案 6 :(得分:10)
我见过大多数人说明确的父母对孩子的投射是不可能的,实际上并非如此。 让我们采取修改后的开始,并尝试通过例子进行证明。
正如我们在.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
结论:首先阅读完毕后,希望现在可以理解如何实现父母到孩子的转换(案例3)。
回答问题:
你的回答是 在案例2 。你可以看到OOP不允许这样的投射,你试图违反OOP的基本规则之一。所以总是选择安全路径。
此外,为了避免这种特殊情况,.net建议使用is/as运算符来帮助您做出明智的决定并提供安全的投射。
答案 7 :(得分:3)
应使用子类的类型创建对象的实例,不能将父类型实例强制转换为子类型
答案 8 :(得分:2)
从C#7.0开始,您可以使用the is keyword来做到这一点:
定义了这些类:
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
这样,您可以确定您的下注将是有效的,并且不会引发异常!
答案 9 :(得分:1)
要进行投射,实际对象必须是类型等于或派生 来自您尝试投射到的类型。 。
或者,以相反的方式说明,您尝试将其强制转换为的类型必须与对象的实际类型相同或基类。
如果您的实际对象是 Baseclass 类型,那么您无法将其强制转换为派生类Type ...
答案 10 :(得分:1)
使用ServiceStack的序列化方法的变体:
var child = baseObject.ConvertTo<ChildType>();
或者更详细:
var child = baseObject.ToJson().FromJson<ChildType>();
ServiceStack的序列化速度可能非常快,但很明显,这不是低延迟传输中大规模转换的解决方案,也不是高度复杂类型的解决方案。对于任何使用ServiceStack的人来说,这可能是显而易见的,但我认为我会在预期评论时澄清。
答案 11 :(得分:1)
至于我,将所有属性字段从基类复制到父类就足够了:
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;
}
可以找到任何对象的通用解决方案here