永久性地将派生类转换为基础

Class A { } Class B : A { } B ItemB = new B(); A ItemA = (A)B; Console.WriteLine(ItemA.GetType().FullName); 

是否可以执行类似上面的操作并让编译器打印出类型A而不是类型B.基本上,是否可以永久地转换对象以使其“丢失”所有派生数据?

您要求的是不可能的,原因有两个:

  1. ItemA.GetType()不返回变量ItemA的编译时类型 – 它返回ItemA 引用的对象的运行时类型
  2. 你无法使(A)B导致表示改变转换(即一个新的A对象),因为用户定义的转换运算符(这里你唯一的希望)不能从派生类转换为基类。 您将获得正常,安全的参考转换。

除此之外,你要求的是非常奇怪的; 人们会认为你真的很难违反Liskov的替代原则。 这里肯定存在一个严重的设计缺陷,你应该解决。

如果你还想这样做; 你可以编写一个方法,通过新建A然后复制数据,从B手动构造A 这可能作为B上的ToA()实例方法存在。

如果你把这个问题描述为“如何从现有的A构造A ?”,那就更有意义了:在A上创建一个复制构造函数,其声明看起来像public A(A a){...} ,这与子类特定的细节无关。 这为您提供了从A的现有实例或其子类之一创建A的一般方法。

我最近遇到了将旧项目迁移到Entity Framework的问题。 如上所述,如果您有来自实体的派生类型,则无法存储它,只能存储基类型。 解决方案是带reflection的扩展方法。

  public static T ForceType(this object o) { T res; res = Activator.CreateInstance(); Type x = o.GetType(); Type y = res.GetType(); foreach (var destinationProp in y.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) { var sourceProp = x.GetProperty(destinationProp.Name); if (sourceProp != null) { destinationProp.SetValue(res, sourceProp.GetValue(o)); } } return res; } 

它不是太整洁,所以如果你真的没有其他选择,请使用它。

为了娱乐,如果你想丢失所有派生数据,你可以这样做:

  class Program { [DataContract(Name = "A", Namespace = "http://www.ademo.com")] public class A { } [DataContract(Name = "A", Namespace = "http://www.ademo.com")] public class B : A { [DataMember()] public string FirstName; } static void Main(string[] args) { B itemB = new B(); itemB.FirstName = "Fred"; A itemA = (A)itemB; Console.WriteLine(itemA.GetType().FullName); A wipedA = WipeAllTracesOfB(itemB); Console.WriteLine(wipedA.GetType().FullName); } public static A WipeAllTracesOfB(A a) { DataContractSerializer serializer = new DataContractSerializer(typeof(A)); using (MemoryStream ms = new MemoryStream()) { serializer.WriteObject(ms, a); ms.Position = 0; A wiped = (A)serializer.ReadObject(ms); return wiped; } } } 

如果您使用调试器,您将看到FirstName在转换为A时仍存储在FirstName字段中,当您从WipeAllTracesOfB返回A时,没有FirstName或任何B的跟踪。

(这可能很明显,但……)

re: @Ani接受的答案 :

如果你还想这样做; 你可以编写一个方法,通过新建A然后复制数据,从B手动构造A。 这可能作为B上的ToA()实例方法存在。

或者使用Automapper将数据复制回来:

 Mapper.CreateMap(); // ...later... var wipedInstance = Mapper.Map(instanceOfChildClass); 

然后wipedInstance.GetType()将是typeof(BaseClass)

不,您不能强制子类型的实例报告它的类型名称是基本类型。

请注意,当您使用指向B类实例的ItemA变量时,您只能使用ItemA变量访问A类中定义的字段和方法。 很少有地方可以实际观察到ItemA指向A类以外的实例的事实 – 在子类中重写的虚拟方法是一种情况,并且运行时类型本身的操作,例如GetType()。

如果你问这个问题,因为当你发送一个类B的实例时,某些代码失败了,当它期望一个A类的实例时,听起来你应该仔细看看那个代码,看看它是什么做错了。 如果它正在测试GetType.LastName,那么它就会被破坏并且脑死亡。 如果它正在测试x IS A则传递B的实例将通过,一切都应该没问题。

转换不会更改对象的类型。 所有的转换方法都是告诉编译器将对象视为什么类型(假设对象实际上是该类型)。

A ItemA = (A)ItemB; 拿着ItemB ,把它看作是A型,并称之为ItemA

当你调用ItemA.GetType()你要求的是真正的类型,而不仅仅是它被视为的类型,真正的类型是B

(我将你的代码示例更改为我认为你的意思,因为你的代码不会编译)