【问题标题】:How to instantiate a derived class from a base class instance?如何从基类实例实例化派生类?
【发布时间】:2021-06-16 09:50:58
【问题描述】:

我想将基类的实例用于初始化/实例化派生类,如下所示:

class B
{
  //...
}

class  D : B
{
   bool DMember; // dummy example
   //...
}

B b =  new B();

// fill b members with values, do stuff
D d = (D)b.Clone(); // wrong, of course

d.DMember = true;

我们已经有了使用MemberwiseClone 克隆B 类的机制。

我实际上尝试在执行的某个点丰富一个类实例,而无需修改太多现有代码。

【问题讨论】:

标签: c# class initialization instance instantiation


【解决方案1】:

是的,但是在 C# 1.0 到 C# 7.3 中这很乏味...

C# 不像 C++ 那样支持隐式复制构造函数。您需要自己手动定义复制构造函数。

...是的,这既乏味又烦人。

类似这样的:

class PersonCommonData
{
    public PersonCommonData(
        Int32 personId,
        String firstName,
        String lastName,
        Date dob
    )
    {
        this.PersonId = personId;
        this.FirstName = firstName;
        this.LastName = lastName;
        this.DoB = dob;
    }

    // Copy-constructor:
    protected PersonCommonData( PersonCommonData copy )
        : this(
            personId: copy.PersonId,
            firstName: copy.FirstName,
            lastName: copy.LastName,
            dob: copy.DoB
        )
    {
    }

    public Int32  PersonId  { get; }
    public String FirstName { get; }
    public String LastName  { get; }
    public Date   DoB       { get; }
}

class EmployeePerson : PersonCommonData
{
    public EmployeePerson( PersonCommonData copy, Int32 employeeId )
        : base( copy )
    {
        this.EmployeeId = employeeId;
    }

    public Int32 EmployeeId { get; }
}

用法:

PersonCommonData common = GetCommonData( personId: 123 );

EmployeePerson e = new EmployeePerson( common, employeeId: 456 );

C# 7 更好的主意:组合

如果您改为组合派生类中的基本公共数据对象,那么这会简化事情:

(请注意,如果您的类是不可变的,这可能只是一个好主意,因为在对象图中拥有可变数据,甚至 可能是可变数据 很少是一个好主意 - 好消息是 C# 9.0 的 record 类型减少了很多乏味,尽管它们并没有消除它)。

class PersonCommonData
{
    public PersonCommonData(
        Int32 personId,
        String firstName,
        String lastName,
        Date dob
    )
    {
        this.PersonId = personId;
        this.FirstName = firstName;
        this.LastName = lastName;
        this.DoB = dob;
    }

    public Int32  PersonId  { get; }
    public String FirstName { get; }
    public String LastName  { get; }
    public Date   DoB       { get; }
}

class EmployeePerson
{
    private readonly PersonCommonData common;

    public EmployeePerson( PersonCommonData common, Int32 employeeId )
    {
        this.common = common ?? throw new ArgumentNullException(nameof(common));
        this.EmployeeId = employeeId;
    }

    public Int32 EmployeeId { get; }

    // Tedium: need to repeat every member as a property-forwarder:
    public Int32  PersonId  => this.common.PersonId;
    public String FirstName => this.common.FirstName;
    public String LastName  => this.common.LastName;
    public Date   DoB       => this.common.DoB;
}

在带有 record 类型和默认接口实现的 C# 9.0 中甚至更好......

如果您只使用不可变数据,那么在 C# 9.0 中,您可以使用 record 类型,从而消除很多乏味。不过,我觉得它们在作曲方面效果最好:

public record PersonCommonData(
    Int32  PersonId,
    String FirstName,
    String LastName,
    Date   DoB
);

public interface IHasPersonCommonData
{
    PersonCommonData Common { get; }

    Int32  PersonId  => this.Common.PersonId;
    String FirstName => this.Common.FirstName;
    String LastName  => this.Common.LastName;
    Date   DoB       => this.Common.DoB;
}

public interface IEmployeePerson : IHasPersonCommonData
{
    Int32 EmployeeId { get; }
}

public record EmployeePerson(
    PersonCommonData Common,
    Int32 EmployeeId
) : IEmployeePerson;

public interface IStudentPerson : IHasPersonCommonData
{
    Int32 StudentId { get; }
}

public partial StudentPerson( PersonCommonData Common, Int32 StudentId ) : IStudentPerson;

但有一些限制——C# 仍然不支持真正的 mixins(接口默认实现仅在通过接口访问类型时才起作用,成员不被继承boo)。

【讨论】:

  • 问题是每当有人要修改基类时,复制构造函数可能也必须更新。另一方面,组合不会轻易适应现有的设计(B类本身是建立在一个接口等)
  • @Gigi 复制构造函数是由 基类定义的 - 所以这里没有 locality 问题:如果基类发生变化,则不需要更改为任何派生类型。
  • @Gigi 如果B 实现了一个接口,那么D 也可以实现相同的接口并将实现转发到B 的内部实例 - 这就是完全的方式组合是为了工作。
猜你喜欢
  • 1970-01-01
  • 2013-05-20
  • 2013-10-15
  • 2019-03-02
  • 2014-09-29
  • 2014-04-27
  • 1970-01-01
  • 2013-05-03
  • 2013-02-12
相关资源
最近更新 更多