无法从父类转换为子类

Pau*_*aul 85 c#

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

Arc*_*rus 111

在C#中向下转换的一种简单方法是序列化父对象,然后将其反序列化为子对象.

 var serializedParent = JsonConvert.SerializeObject(parentInstance); 
 Child c  = JsonConvert.DeserializeObject<Child>(serializedParent);
Run Code Online (Sandbox Code Playgroud)

我有蒙上动物为狗一个简单的控制台应用程序,使用上面的代码两行过这里

  • 好吧,我会毫不犹豫地称之为"沮丧". (17认同)
  • 我想知道与创建派生类型的新实例并从基类的属性分配其属性相比,这样的序列化要慢多少倍。 (6认同)
  • 我喜欢当有人在盒子外面思考并让人们沉默时告诉OP它无法做到(除了一两个巨魔)!感谢您对此的协助.我一直试图在过去几个小时内解决这个问题:) (4认同)
  • 这是一个很好的解决方案.我有一个案例,我的子类只是一个没有附加功能的父类的包装器.我这样做了所以我没有将web引用导入我的应用程序,因为它在我的帮助库中.这允许我将父转换为我的包装类.谢谢! (3认同)
  • 经过测试,可以使用. (2认同)
  • 有效的解决方案,但性能下降 (2认同)
  • 你是个天才!:) (2认同)

cjk*_*cjk 110

你不能把哺乳动物变成狗 - 它可能是一只猫.

你不能把食物撒成三明治 - 它可能是一个芝士汉堡.

你不能把汽车变成法拉利 - 它可能是本田,或者更具体地说,你不能将法拉利360摩德纳投射到法拉利360 Challange Stradale--虽然它们都是法拉利360,但它们有不同的部分.

  • 可以理解的障碍,因此不可能以这种方式实际"铸造".但是,如果他想要一只眼睛的颜色/重量/头发模式/年龄等与哺乳动物体内的猫一样的狗呢?基本上复制公共属性. (17认同)
  • FastAl,这正是我们拥有接口的原因.哺乳动物必须实施IMammal并包含眼睛颜色,重量等.现在你可以将狗和猫都投射到IMammal. (5认同)

Gre*_*g D 56

基类引用所引用的实例不是子类的实例.没有错.

进一步来说:

Base derivedInstance = new Derived();
Base baseInstance = new Base();

Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException
Run Code Online (Sandbox Code Playgroud)

为了使转换成功,您正在向下转换的实例必须是您正在向下转换的类的实例(或者至少,您要向下转换的类必须在实例的类层次结构中),否则演员会失败.

  • @pradeeptp:当然是构建的.谁说了编译错误? (6认同)

Meh*_*ANI 17

在某些情况下,这样的演员会有意义.
我的情况是,我通过网络收到了一个BASE课程,我需要更多的功能.因此得出它来处理它我所需要的所有铃声和口哨,并将收到的BASE类转换为DERIVED一个根本不是一个选项(抛出InvalidCastException of Course)

一个实用的开箱即用的解决方案是声明一个EXTENSION Helper类,它实际上并没有继承BASE类,而是将其作为成员包含在内.

public class BaseExtension
{
   Base baseInstance;

   public FakeDerived(Base b)
   {
      baseInstance = b;
   }

   //Helper methods and extensions to Base class added here
}
Run Code Online (Sandbox Code Playgroud)

如果你有松耦合的,只是需要一些额外的功能,以基础课,而不会真的有推导的绝对需要,这可能是一个快速和简单的解决方法.


par*_*ent 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>();
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

当应用程序启动时AutoMapperConfiguration.Configure(),您可以像这样投影:

ChildClassOne child = Mapper.Map<BaseClass, ChildClassOne>(baseClass);
Run Code Online (Sandbox Code Playgroud)

属性按约定映射,因此如果继承类,则属性名称完全相同,并自动配置映射.您可以通过调整配置来添加其他属性.请参阅文档.


Mal*_*lil 11

我已经看到大多数人都说不明确父母对孩子的投射是不可能的,实际上并非如此.让我们采取修改后的开始,并尝试通过示例证明它.

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

  1. 对于值类型
  2. 对于引用类型(在您的情况下为其引用类型)

参考类型还有三个主要的情境案例,其中任何情景都可能存在.

孩子到父母(隐性施法 - 总是成功)

案例1.任何直接或间接父母的孩子

Employee e = new Employee();
Person p = (Person)e; //Allowed
Run Code Online (Sandbox Code Playgroud)

父母对孩子(明确的演员 - 可以成功)

案例2.父变量持有父对象(不允许)

Person p = new Person();  // p is true Person object
Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue
Run Code Online (Sandbox Code Playgroud)

案例3.包含子对象的父变量(始终成功)

注意:由于对象具有多态性,因此父类类型的变量可以保存子类型.

Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed
Run Code Online (Sandbox Code Playgroud)

结论:首先阅读之后,希望它现在有意义,如父母到孩子的转换是可能的(案例3).

回答问题:

你的答案是在案例2中.你可以看到OOP不允许这样的投射而你试图违反OOP的基本规则之一.所以总是选择安全路径.

此外,为了避免这种特殊情况,.net建议使用is/as运算符来帮助您做出明智的决策并提供安全的投射.


Fas*_*tAl 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
Run Code Online (Sandbox Code Playgroud)

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

一般来说,强制转换会创建两个指向同一对象的变量(这里的迷你教程,请不要向我提出异常案例例外).这有很大的后果(对读者的锻炼)!

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

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