【问题标题】:Convert base class to derived class [duplicate]将基类转换为派生类[重复]
【发布时间】:2012-09-15 22:54:21
【问题描述】:

是否可以在 C# 中将基类对象显式转换为其派生类之一?目前认为我必须为我的派生类创建一个构造函数,该构造函数接受基类对象作为参数并复制属性值。我不太喜欢这个主意,所以我想尽可能避免它。

这似乎不应该工作(对象被实例化为新基,因此不应为派生类的额外成员分配内存)但 C# 似乎允许我这样做:

class BaseClass
{
  ... some stuff ...
}

class DerivedClass : BaseClass
{
    public bool MyDerivedProperty{ get; set; }
}


static void Main(string[] args)
{
    BaseClass myBaseObject = new BaseClass();
    DerivedClass myDerivedObject = myBaseObject as DerivedClass;

    myDerivedObject.MyDerivedProperty = true;
}

【问题讨论】:

  • 你会在最后一行得到一个空引用异常。
  • 已经在这里回答:stackoverflow.com/questions/729527/…
  • 您应该了解何时适合使用“as”与常规演员表。如果你在这里使用强制转换,C# 编译器会告诉你你在做什么是错误的。
  • 我投票决定重新打开,因为标记的“重复”是一个完全不同的问题。这个问题是关于转换的,而“重复”的问题是关于铸造
  • 最好最快的选择是使用 JsonConvert 你可以在原始问题上找到我的答案

标签: c# inheritance casting downcast


【解决方案1】:

不,没有像你说的那样转换类的内置方法。最简单的方法是按照您的建议进行操作:创建一个 DerivedClass(BaseClass) 构造函数。其他选项基本上会出现以自动将属性从基础复制到派生实例,例如使用反射。

您使用as 发布的代码将会编译,我相信您已经看到了,但是当您运行它时会抛出一个空引用异常,因为myBaseObject as DerivedClass 将评估为null,因为它不是DerivedClass 的一个实例。

【讨论】:

    【解决方案2】:

    这是不可能的。但是你可以使用像AutoMapper这样的对象映射器

    编辑

    我想推荐一个更简单的对象映射器:TinyMapper。 AutoMapper 现在使用起来非常复杂。我不再使用它了。 TinyMapper 涵盖了大多数用例,而且更加简单且超级快速。

    例子:

    //In app startup
    TinyMapper.Bind<Person, PersonDto>();
    
    //Usage
    var personDto = TinyMapper.Map<PersonDto>(person);
    

    AutoMapper(我认为是旧版本)的示例,供仍然想使用它的人使用:

    class A
    {
        public int IntProp { get; set; }
    }
    class B
    {
        public int IntProp { get; set; }
        public string StrProp { get; set; }
    }
    

    在 global.asax 或应用程序启动中:

    AutoMapper.Mapper.CreateMap<A, B>();
    

    用法:

    var b = AutoMapper.Mapper.Map<B>(a);
    

    它可以通过流畅的 API 进行配置。

    【讨论】:

    • 这很好用,但你必须先初始化它 AutoMapper.Mapper.Initialize(cfg => cfg.CreateMap());
    • AutoMapper中的DO和DONT参考官方2019年发布的AutoMapper Usage Guidelines
    • 从 9.0 版开始不再支持 AutoMapper 静态 API。您将需要执行以下操作: var configuration = new MapperConfiguration(cfg => { cfg.CreateMap(); }); var mapper = new Mapper(配置); var b = mapper.Map(a);然后最好将配置或映射器存储在依赖容器中。
    • 这是很好的信息。我将此添加为切线:我正在寻找一种在 MVC 中使用 AutoMapper v 9.0+ 的方法。我最终在 Global.asax.cs 中创建了一个静态属性,并在 Application_Start 期间将配置的映射器分配给它。希望对可能没有考虑过这种方法的人有所帮助。在 .net Core 中,您可以只将映射器注入到您的控制器实例中,但这在“传统”.net 框架中不可用。
    • 一般应避免使用 Automapper 之类的 Voodo。它使用反射来运行槽属性,并且与其他解决方案(如编写 a.prop1=b.pro1、a.pro2=b.prop2 等)相比,通常使用起来很昂贵
    【解决方案3】:

    我找到了一种解决方案,并不是说它是最好的,但我觉得它很干净,不需要对我的代码进行任何重大更改。在我意识到它不起作用之前,我的代码看起来和你的很相似。

    我的基类

    public class MyBaseClass
    {
       public string BaseProperty1 { get; set; }
       public string BaseProperty2 { get; set; }
       public string BaseProperty3 { get; set; }
       public string BaseProperty4 { get; set; }
       public string BaseProperty5 { get; set; }
    }
    

    我的派生类

    public class MyDerivedClass : MyBaseClass
    {
       public string DerivedProperty1 { get; set; }
       public string DerivedProperty2 { get; set; }
       public string DerivedProperty3 { get; set; }
    }
    

    上一个获取填充基类的方法

    public MyBaseClass GetPopulatedBaseClass()
    {
       var myBaseClass = new MyBaseClass();
    
       myBaseClass.BaseProperty1 = "Something"
       myBaseClass.BaseProperty2 = "Something else"
       myBaseClass.BaseProperty3 = "Something more"
       //etc...
    
       return myBaseClass;
    }
    

    在我尝试这个之前,这给了我一个无法投射的错误

    public MyDerivedClass GetPopulatedDerivedClass()
    {
       var newDerivedClass = (MyDerivedClass)GetPopulatedBaseClass();
    
       newDerivedClass.UniqueProperty1 = "Some One";
       newDerivedClass.UniqueProperty2 = "Some Thing";
       newDerivedClass.UniqueProperty3 = "Some Thing Else";
    
       return newDerivedClass;
    }
    

    我如下更改了我的代码,现在它似乎可以工作并且更有意义:

    public MyBaseClass GetPopulatedBaseClass()
    {
       var myBaseClass = new MyBaseClass();
    
       myBaseClass.BaseProperty1 = "Something"
       myBaseClass.BaseProperty2 = "Something else"
       myBaseClass.BaseProperty3 = "Something more"
       //etc...
    
       return myBaseClass;
    }
    

    public void FillBaseClass(MyBaseClass myBaseClass)
    {
       myBaseClass.BaseProperty1 = "Something"
       myBaseClass.BaseProperty2 = "Something else"
       myBaseClass.BaseProperty3 = "Something more"
       //etc...
    }
    

    public MyDerivedClass GetPopulatedDerivedClass()
    {
       var newDerivedClass = (MyDerivedClass)GetPopulatedBaseClass();
    
       newDerivedClass.UniqueProperty1 = "Some One";
       newDerivedClass.UniqueProperty2 = "Some Thing";
       newDerivedClass.UniqueProperty3 = "Some Thing Else";
    
       return newDerivedClass;
    }
    

    public MyDerivedClass GetPopulatedDerivedClass()
    {
       var newDerivedClass = new MyDerivedClass();
    
       FillBaseClass(newDerivedClass);
    
       newDerivedClass.UniqueProperty1 = "Some One";
       newDerivedClass.UniqueProperty2 = "Some Thing";
       newDerivedClass.UniqueProperty3 = "Some Thing Else";
    
       return newDerivedClass;
    }
    

    【讨论】:

    • 非常好!正是我想要的!谢谢:D
    • 每次添加属性时,您都必须对其进行编辑。因此,对于其他阅读本文的人,我建议您考虑改用合成
    【解决方案4】:

    您可以自己实现转换,但我不建议这样做。如果您想这样做以扩展现有对象的功能,请查看Decorator Pattern

    【讨论】:

      【解决方案5】:

      不,没有内置的转换。 您需要创建一个构造函数,就像您提到的那样,或其他一些转换方法。

      另外,由于 BaseClass 不是 DerivedClass,myDerivedObject 将为 null,并且上面的最后一行将抛出 null ref 异常。

      【讨论】:

        【解决方案6】:

        不,这是不可能的。唯一可行的方法是

        static void Main(string[] args)
        {
            BaseClass myBaseObject = new DerivedClass();
            DerivedClass myDerivedObject = myBaseObject as DerivedClass;
        
            myDerivedObject.MyDerivedProperty = true;
        }
        

        【讨论】:

        • 这会引发 NullReferenceException,因此无法按说明工作
        • 谢谢,其他答案没有说明如何投射
        • 有时候,稍微反思一下,就可以避免重大问题。谢谢!
        • 但这不起作用(正如 ahmed 所说,它会引发空引用异常)
        • 这里没有发生转换。基类指针 (myBaseObject) 被简单地设置为 DerivedClass 的一个实例。从头到尾它仍然是一个 DerivedClass。
        猜你喜欢
        • 2017-01-08
        • 2013-11-17
        • 1970-01-01
        • 2021-02-18
        • 2017-07-18
        • 2011-02-04
        相关资源
        最近更新 更多