24
votes

The C# 9 records feature specification includes the following:

A record type contains two copying members:

A constructor taking a single argument of the record type. It is referred to as a "copy constructor". A synthesized public parameterless instance "clone" method with a compiler-reserved name

But I cannot seem to call either of these two copying members:

public record R(int A);
// ...
var r2 = new R(r); // ERROR: inaccessible due to protection level
var r3 = r.Clone(); // ERROR: R does not contain a definition for Clone

From this, I understand that the constructor is protected and thus can't be accessed outside the record's inheritance hierarchy. And so we're left with code like this:

var r4 = r with { };

But what about cloning? The clone method is public according to the specification above. But what is its name? Or is it an effectively random string so that it should not be called outside the record's inheritance hierarchy? If so, what is the correct way to deep copy records? It seems from the specification that one is able to create one's own clone method. Is this so, and what would be an example of how it should work?

1
"But what about cloning?" That's what the with expression does. You're not supposed to know the name of the "clone" method. That's what "compiler-reserved" means. It's there to support with expressions.madreflection
Implementation detail on the table: the name is <Clone>$. By design, you can't call it yourself.madreflection
It does not. This cloning mechanism is a shallow copy. The document you linked doesn't contain the word "deep" anywhere. If you want a deep copy, you'll need to implement that yourself.madreflection
@madreflection Thank you. I think what confused me was the use of "copy" and "clone" in the documentation, but you are correct that the word "deep" is not used, so that must be implemented in some other way. I was also confused by the documentation's statement "If a virtual "clone" method is present in the base record, ..." As I understand it now, that would always be a synthetically created Clone() method, not a user-defined one; attempting to create such a method oneself generates an error.mbabramo
It should bo noted imo, that 'shallow copy' is a well-defined concept, whereas 'deep copy' is not.TaW

1 Answers

22
votes

But what about cloning?

var r4 = r with { };

performs a shallow clone on r.

The clone method is public according to the specification above. But what is its name?

The C# compiler has a fairly common trick where it gives generated members names which are illegal in C#, but legal in IL, so that they can't be called except from the compiler, even if they're public. In this case the name of the Clone method is <Clone>$.

If so, what is the correct way to deep copy records?

Deep copying you're out of luck. However since records should ideally be immutable, there should be no difference in practice between a shallow copy, a deep copy, and the original instance.

It seems from the specification that one is able to create one's own clone method. Is this so, and what would be an example of how it should work?

Unfortunately this didn't make the cut for C# 9, but there's a strong chance it'll be in C# 10.