Class A { }
Class B : A { }
B ItemB = new B();
A ItemA = (A)B;
Console.WriteLine(ItemA.GetType().FullName);
是否可以执行类似上面的操作并让编译器打印出类型A而不是类型B.基本上,是否可以永久地转换对象以使其“丢失”所有派生数据?
答案 0 :(得分:10)
你要求的是不可能的,原因有两个:
ItemA.GetType()
不返回变量ItemA
的编译时类型 - 它返回引用的运行时类型 ItemA
。 (A)B
导致表示变化转换(即新的A
对象),因为用户定义的转换运算符(这里你唯一的希望)无法从派生转换为基数 - 班。您将获得正常,安全的参考转换。除此之外,你要求的是非常奇怪的;人们会认为你真的很难违反Liskov的替代原则。这里几乎肯定存在一个严重的设计缺陷,你应该解决。
如果你还想这样做;您可以编写一种方法,通过新建A
然后复制数据,从B
手动构建A
。这可能以ToA()
的形式存在
B
上的实例方法。
如果您将此问题描述为“我如何从现有的 A ?构建A”,则更有意义:在A
上创建一个复制构造函数,其中声明看起来像public A(A a){...}
,它与子类特定的细节无关。这为您提供了从A
或其子类之一的现有实例创建A
的一般方法。
答案 1 :(得分:10)
我最近遇到了将旧项目迁移到Entity Framework的问题。如上所述,如果您有来自实体的派生类型,则无法存储它,只能存储基类型。 解决方案是带反射的扩展方法。
public static T ForceType<T>(this object o)
{
T res;
res = Activator.CreateInstance<T>();
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;
}
它不是太整洁,所以如果你真的没有其他选择,请使用它。
答案 2 :(得分:6)
为了娱乐,如果你想丢失所有的派生数据,你可以这样做:
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的跟踪。
答案 3 :(得分:2)
(这可能很明显,但是......)
如果你还想这样做;你可以编写一个方法,通过新建A然后复制数据,从B手动构造A。这可能作为B上的ToA()实例方法存在。
或使用Automapper复制数据:
Mapper.CreateMap<ChildClass, BaseClass>();
// ...later...
var wipedInstance = Mapper.Map<BaseClass>(instanceOfChildClass);
然后wipedInstance.GetType()
将为typeof(BaseClass)
答案 4 :(得分:1)
不,您不能强制子类型的实例报告它的类型名称是基本类型。
请注意,当您使用指向B类实例的ItemA变量时,您只能使用ItemA变量访问A类中定义的字段和方法。很少有地方可以实际看到ItemA指向A类以外的实例的事实 - 在子类中重写的虚拟方法是一种情况,并且运行时类型本身的操作,例如GetType()。
如果你问这个问题是因为当你想要一个A类实例时,当你发送一个B类实例时某些代码失败了,听起来你应该仔细看看那个代码到看看它做错了什么。如果它正在测试GetType.LastName,那么它就会崩溃并且脑死亡。如果它正在测试x IS A
那么传递B的实例将会通过,一切都应该没问题。
答案 5 :(得分:0)
转换不会更改对象的类型。所有的转换方法都是告诉编译器将对象视为什么类型(假设对象实际上是该类型)。
A ItemA = (A)ItemB;
正在说ItemB
,将其视为A
类型,并将其称为ItemA
当你致电ItemA.GetType()
时,你要求的是真实的类型,而不仅仅是它被视为的类型,真正的类型是B
(我将你的代码示例更改为我认为你的意思,因为你的代码不会编译)