如何从基类实例实例化派生类?

时间:2021-06-16 09:50:58

标签: c# class initialization instance instantiation

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

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;

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

我实际上尝试在执行的某个时刻丰富一个类实例,而不对现有代码进行太多修改。

1 个答案:

答案 0 :(得分: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# 仍然不支持真正的 mixin(接口默认实现仅在通过接口访问类型时才有效,成员不会被继承boo)。