我试图将一个父类强制转换为子类,但是却收到了一个InvalidCastException的异常。子类只有一个int类型的属性。有人知道我需要做什么吗?
我试图将一个父类强制转换为子类,但是却收到了一个InvalidCastException的异常。子类只有一个int类型的属性。有人知道我需要做什么吗?
在C#中进行向下转型的简单方法是将父类进行序列化,然后将其反序列化为子类。
var serializedParent = JsonConvert.SerializeObject(parentInstance);
Child c = JsonConvert.DeserializeObject<Child>(serializedParent);
我有一个简单的控制台应用程序,使用上面两行代码将动物转换为狗,参考这里
你不能把哺乳动物强制变成一只狗 - 它可能是只猫。
你不能把食物强行变成三明治 - 它可能会是个芝士汉堡。
你不能把一辆车强行变成法拉利 - 它可能是本田,更具体地说,你不能把法拉利360 Modena强制转换为法拉利360 Challange Stradale - 它们有不同的部件,尽管它们都是法拉利360。
你的基类引用所指向的实例不是你子类的实例。这没有问题。
更具体地说:
Base derivedInstance = new Derived();
Base baseInstance = new Base();
Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException
为了使强制类型转换成功,你需要将要进行强制类型转换的实例(downcasting)确保是你所要转换成的类的实例(或者至少是该类的继承链中的一个),否则强制类型转换会失败。
Derived good = (Derived)derivedInstance;
Derived fail = (Derived)baseInstance;
这段代码在 .NET 3.5 中可以编译通过,你说的问题在哪里? - pradeeptp我看到很多人说显式的父子类型转换是不可能的,但实际上这并不是真的。让我们重新开始,并尝试通过示例来证明它。
正如我们所知,在.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运算符,这些运算符将帮助您做出明智的决策并提供安全转换。
在一些情况下,这样的转换是有意义的。
就我个人而言,我从网络上接收到了一个基类,并且需要为其添加更多功能。因此,在我的端上派生出一个包含所有我想要的特性的派生类,并将接收到的基类转换为派生类并不是一个选项(当然会抛出 InvalidCastException 异常)
一个实用的“开箱即用”解决方案是声明一个扩展 Helper 类,它实际上并没有继承基类,而是将其作为成员包含进来。
public class BaseExtension
{
Base baseInstance;
public FakeDerived(Base b)
{
baseInstance = b;
}
//Helper methods and extensions to Base class added here
}
如果您的代码松耦合,并且只需要向基类添加一些额外特性,而不是真正需要继承,那么这可能是一个快速简单的解决方法。
BaseExtension
至少实现 IBase
,以便在类似的上下文中使用它?或者这对你的需求不重要? - tobriand那样会违反面向对象的原则。我认为在这里和项目的其他地方,一个优雅的解决方案是使用对象映射框架,比如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);
属性是按照惯例映射的,因此如果类被继承,则属性名称完全相同,并且映射会自动配置。您可以通过调整配置来添加其他属性。请参见文档。
保罗,你没有问“我能做到吗”——我假设你想知道如何做到!
我们在一个项目中不得不这样做——我们设置了许多类,以一种通用的方式进行一次设置,然后初始化特定于派生类的属性。我使用VB,所以我的示例是VB(很难受),但我从这个网站窃取了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
对象的实例应该使用子类的类型创建,你不能将父类型的实例转换为子类型
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
这样你就可以确保你的降级将是有效的,不会抛出异常!
对我来说,只需将基类的所有属性字段复制到父类中即可,如下所示:
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;
}
可以在这里找到适用于任何对象的通用解决方案。