Inheritance
The CloneFromTo chain
Every [Cloneable] type contributes a helper that copies only the members it declares and chains to its base:
protected static void CloneFromTo(PhoneNumber from, PhoneNumber to)
{
ContactMethod.CloneFromTo(from, to);
to.CountryCode = from.CountryCode;
to.LocalNumber = from.LocalNumber;
}
Every concrete type additionally gets a factory that news up the instance and runs the chain:
public MobileNumber CloneMobileNumber()
{
var from = this;
var to = new MobileNumber {Carrier = from.Carrier};
CloneFromTo(from, to);
return to;
}
public override PhoneNumber Clone() => CloneMobileNumber();
Each helper can copy private and protected state of the type that declares it.
required and init-only members are collected from the whole inheritance chain and set in the leaf's object initializer.
Which type owns the Clone() name
The clone root is the topmost ancestor that declares a clone interface or a Clone() method of its own.
- The root gets
Clone(), returning the root type. - Everything below it gets
Clone+ its own type name, plus anoverrideofClone().
[Cloneable] public abstract partial class ContactMethod {}
[Cloneable] public abstract partial class PhoneNumber : ContactMethod, ICloneable<PhoneNumber> {}
[Cloneable] public partial class MobileNumber : PhoneNumber {}
| Type | Generated |
|---|---|
ContactMethod |
CloneFromTo only |
PhoneNumber |
public abstract PhoneNumber Clone(); + CloneFromTo |
MobileNumber |
CloneMobileNumber(), public override PhoneNumber Clone() => CloneMobileNumber(); + CloneFromTo |
A type that is [Cloneable] but declares no clone interface and no Clone() of its own — ContactMethod above — only contributes a helper, leaving the Clone() name to its subclasses. Such a type cannot be cloned polymorphically: a member typed ContactMethod is copied by reference, with a warning. Declare a clone interface on the base wherever you need that.
Use [Cloneable(MethodName = "...")] where the default name does not match an existing public method you want to keep.
Virtual by default
The generated root Clone() is virtual unless the class is sealed.
Every base class must opt in
If a base type holds copyable state but is not [Cloneable], you get a compiler error rather than a clone that silently drops that state. Annotate the base type too.