Runtime: Suggestion: Generic ICloneable<T> interface

Created on 7 Jun 2020  路  12Comments  路  Source: dotnet/runtime

The current non-generic ICloneable interface requires casting the returned object type to whatever type is expected.
I think it'd be good to see ICloneable interface and implement it in the classes that currently implements ICloneable.

public interface ICloneable<T> where T : class
{
    T Clone();
}
api-suggestion area-System.Runtime untriaged

Most helpful comment

ICloneable is widely considered to have been a mistake. I don't expect we'd want to double-down on it with a generic counterpart.

e.g. from the docs:
https://docs.microsoft.com/en-us/dotnet/api/system.icloneable?view=netcore-3.1
"Because callers of Clone() cannot depend on the method performing a predictable cloning operation, we recommend that ICloneable not be implemented in public APIs."

All 12 comments

I couldn't figure out the best area label to add to this issue. Please help me learn by adding exactly one area label.

ICloneable is widely considered to have been a mistake. I don't expect we'd want to double-down on it with a generic counterpart.

e.g. from the docs:
https://docs.microsoft.com/en-us/dotnet/api/system.icloneable?view=netcore-3.1
"Because callers of Clone() cannot depend on the method performing a predictable cloning operation, we recommend that ICloneable not be implemented in public APIs."

Sounds reasonable to not implement an ICloneable<T> interface. But for the current classes that already implements ICloneable, wouldn't it be good to add a Clone method (without ICloneable interface) that returns the exact type instead of object?

My idea is that, whether it was a mistake or not, some classes already implemented ICloneable. So, having a Clone method (without a new generic interface) that returns the exact type wouldn't be bad.

So, having a Clone method (without a new generic interface) that returns the exact type wouldn't be bad.

Which types specifically? And for each type, where are some examples where that Clone method would be valuable?

Example.

If the following is added in addition to the current Clone:

public TransferCodingHeaderValue Clone()
{
    return new TransferCodingHeaderValue(this);
}

it will add two benefits (surely, I'm not a professional and may be wrong):

  1. Internal benefit - The this.Parameters.Add((NameValueHeaderValue)((ICloneable)parameter).Clone()); line will be simplified and the casting will be avoided.
  2. External benefit - For the class consumers who might be doing a call similar to the previously mentioned, they can simplify it too.

Internal benefit

The parameter there isn't the type you mention. And a new public API isn't required in order to simplify/improve this implementation detail (though going through the interface there is wasteful and unnecessary).

External benefit

Can you point to an example for this specific type?

The parameter there isn't the type you mention

Yes, but the idea is same. However, I agree that this can be improved without a new public API. (Should I try to capture the Clone calls that could be improved and submit a PR?)

Can you point to an example for this specific type?

Unfortunately, no. But I expect that if the Clone is public, there could be someone who uses it for whatever reason.

But I expect that if the Clone is public, there could be someone who uses it for whatever reason.

Maybe. But adding new public surface area is not cheap. In addition to the effort required to design/implement/test/document/etc., it has on-going costs around maintenance, it adds to the conceptual overhead of consuming the type, and it can even make it harder to evolve the type by placing additional restrictions on what can be achieved... so there needs to be a strong, demonstrated case for why the benefits outweigh the negatives. On top of all of that, there's a question of semantics. What would Clone do here? Is it a shallow clone, or does it deeply copy the contained collections? That same issue is one of the reasons ICloneable failed.

Should I try to capture the Clone calls that could be improved and submit a PR?

Is there any real scenario this is meaningfully impacting?

Should I try to capture the Clone calls that could be improved and submit a PR?

Is there any real scenario this is meaningfully impacting?

Wouldn't using new SomeClass(someObjectOfTheSameSomeClass) directly perform better than calling (SomeClass)((ICloneable)someObjectOfTheSameSomeClass).Clone()?

Changing this internally without adding a new API wouldn't be problematic I think?

Wouldn't using new SomeClass(someObjectOfTheSameSomeClass) directly perform better than calling (SomeClass)((ICloneable)someObjectOfTheSameSomeClass).Clone()?

It's not necessarily the same thing. Imagine if someObjectOfTheSameSomeClass was actually an instance of type B that derived from type A, but you had a reference to it as A. Your proposed new A(b) would actually produce an A, whereas (A)((ICloneable)b).Clone()) would produce a B.

And even if it did perform slightly better and wasn't flawed, the question is, who does that actually help? If it can be done safely with as little code, then great. But I suspect you'd need to introduce more code to get correct behavior for what you're talking about, at which point you're adding code and complexity, and how much are you actually improving performance and in what real scenario? That's why I'm asking about a real scenario, i.e. show the code in a real app that's going to benefit from this meaningfully.

It's not necessarily the same thing. Imagine if someObjectOfTheSameSomeClass was actually an instance of type B that derived from type A, but you had a reference to it as A. Your proposed new A(b) would actually produce an A, whereas (A)((ICloneable)b).Clone()) would produce a B.

That's a great point I wasn't considering.

And even if it did perform slightly better and wasn't flawed, the question is, who does that actually help? If it can be done safely with as little code, then great. But I suspect you'd need to introduce more code to get correct behavior for what you're talking about, at which point you're adding code and complexity, and how much are you actually improving performance and in what real scenario? That's why I'm asking about a real scenario, i.e. show the code in a real app that's going to benefit from this meaningfully.

If the change is small and improving performance, My opinion might be to just go with it. But with the case you mentioned, the code to get things correct might be more complex. So I'm completely agreeing that it doesn't worth it without a real scenario.

Finally, Really really thanks for taking the time discussing this. I appreciate that very much.

Was this page helpful?
0 / 5 - 0 ratings