无法从父类转换为子类

时间:2009-06-12 19:39:43

标签: c#

我正在尝试从父类转换为子类,但是我得到了InvalidCastException。子类只有一个int类型的属性。有谁知道我需要做什么?

12 个答案:

答案 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#版本:

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

当然这不是真的。它正在创建一个新的派生对象并从父项复制属性,将子属性保留为空。这就是我需要做的一切,听起来就像你需要做的一切。请注意,它只复制属性,而不是类中的成员(公共变量)(但是如果你因为暴露公共成员而感到羞耻,你可以扩展它)。

一般来说,Casting会创建两个指向同一个对象的变量(这里的迷你教程,请不要向我抛出异常案例例外)。这有很大的后果(向读者练习)!

当然,我必须说明为什么语言不会让你从基地出发来推导实例,而是以另一种方式。想象一下你可以获取winforms文本框(派生)的实例并将其存储在Winforms控件类型的变量中的情况。当然,'控件'可以将对象移动到OK,你可以处理关于文本框的所有'controll-y'事物(例如,top,left,.text属性)。如果没有将指向内存中文本框的'control'类型变量强制转换,则无法看到特定于文本框的内容(例如.multiline),但它仍然存在于内存中。

现在想象一下,你有一个控件,并且你想要一个类型为textbox的变量。记忆中的控制缺少“多线”和其他文字的东西。如果您尝试引用它们,控件将不会神奇地增长多行属性!属性(在这里看它就像一个成员变量,实际上存储一个值 - 因为文本框实例的内存中存在)必须存在。因为你正在施法,记住,它必须是你指向的同一个物体。因此,它不是一种语言限制,在这种情况下在哲学上是不可能的。

答案 6 :(得分:10)

我见过大多数人说明确的父母对孩子的投射是不可能的,实际上并非如此。 让我们采取修改后的开始,并尝试通过例子进行证明。

正如我们在.net中所知,所有铸件都有两大类。

  1. 对于值类型
  2. 对于参考类型(在您的情况下是其引用类型)
  3. 参考类型还有三个主要的情境案例,其中任何情景都可能存在。

    Child to Parent(隐式投射 - 总是成功)

      

    案例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