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

Tel*_*ian 20 .net c# type-conversion

Class A { }
Class B : A { }

B ItemB = new B();
A ItemA = (A)B;

Console.WriteLine(ItemA.GetType().FullName);
Run Code Online (Sandbox Code Playgroud)

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

小智 11

我最近遇到了将旧项目迁移到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;
    }
Run Code Online (Sandbox Code Playgroud)

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


Ani*_*Ani 10

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

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

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

如果你还想这样做; 你可以写手动构建一个方法A,从一个B由newing了一个A过来,然后复制数据.这可能作为ToA() 实例方法存在B.

如果您将此问题描述为"如何从现有A构造A?",则更有意义:创建一个复制构造函数A,其声明类似public A(A a){...},与子类特定的详细信息无关.这为您提供了A从现有实例A或其子类之一创建的一般方法.

  • 我并没有真正违反任何原则,因为我只是要求编译器忘记该项目有任何派生信息.这就像忘记了一个对象是一个Employee并将它们视为一个Person.我的主要目标是绕过WCF的派生类型问题. (14认同)
  • @Telavian你并不孤单.这也是EF的问题.如果你有一个类型派生自EF中映射的类型并尝试更新该对象,EF会抱怨它不知道如何映射它**因为派生类型没有映射**,但基类型是. (4认同)
  • 我经常看到这种违规论点,但是@Yuck提出了我可以想到的最常见原因。我们经常出于dto的目的扩展实体类,并获得相同的dto予以执行。英孚希望对基层采取行动;它对派生类一无所知。这是否违反了一些学术上的OOP概念?也许。完成工作了吗?绝对。从现在开始,有人会在乎吗?不。 (2认同)

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

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