-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
We should obsolete the String.Copy method #27515
Comments
string.Create(int length, TState state, SpanAction<char, TState> action) formalizes the mutate newly allocated zero'd string prior to use pattern; while introducing safety via the Span and ensuring its not used prior to mutation. For passing to native code which may mutate the string; shouldn't one of the Marshal.StringToXxx methods (+ free) be used instead for a defensive copy? This info could be included in the obsoletion message. |
Could somebody please clarify why the |
What clarification are you looking for? It's equivalent to if
Yes. The whole point of this usage is so that a method like If you just want to slice up some existing span and create a string from it, you can just use |
Thank you for the clarification. So the original intention was to have a preallocated memory + writable memory window (Span) and allow the delegate to fill it. Thats perfectly fine. The only missing piece to me is how / why C# allowed to mutate the passed in argument (the writable memory window itself in the line: string GetAsciiString(ReadOnlySequence<byte> buffer)
{
if (buffer.IsSingleSegment)
{
return Encoding.ASCII.GetString(buffer.First.Span);
}
return string.Create((int)buffer.Length, buffer, (span, sequence) =>
{
foreach (var segment in sequence)
{
Encoding.ASCII.GetChars(segment.Span, span);
span = span.Slice(segment.Length);
}
});
}
|
The span is passed in by value, so its just mutating the function local argument; Without mutating the argument the example method would look like the following string GetAsciiString(ReadOnlySequence<byte> buffer)
{
if (buffer.IsSingleSegment)
{
return Encoding.ASCII.GetString(buffer.First.Span);
}
return string.Create((int)buffer.Length, buffer, (span, sequence) =>
{
Span<char> output = span;
foreach (var segment in sequence)
{
Encoding.ASCII.GetChars(segment.Span, output);
output = output.Slice(segment.Length);
}
});
} Mutating the argument just skips the copy Span<char> output = span; |
@benaadams Thank you for the clarification. So the missing piece was a Roslyn optimization for function local argument mutation. |
Yeah, the Span is initially pointing at the window over the allocated string (of size The It doesn't want to be passed |
Tracked by dotnet/corefx#33602 |
I am a little shocked seeing that Copy method is now obsolete :| What about deep cloning a object that contains strings? We can do now new string(oldStr), but was it only just some kind of weird cleaning that I don't understand? |
Strings are immutable. Why do you need to create a copy? |
Do you have a tool that walks an object graph looking for the |
The reason you want to create a copy is simple: let's say you load a value from a resource string. And you want to go and do find and replace within that string. Every iteration of a loop you copy the original string that was expensive to get out of resources so you got it before the string, and then you do find and replace on the original string. This happens all of the time. |
Find and replace operations always return a copy of the string. You don't need to make a manual copy. Example: string a = "Hello world!";
string b = a.Replace("Hello", "Goodnight");
Console.WriteLine(a); // "Hello world!" <-- still keeps its original value
Console.WriteLine(b); // "Goodnight world!" <-- new string with mutated value |
I thought this wasn't the case with StringBuilder? (i.e. create a stringbuilder off of a string, then act on it, I thought it was always working with pointers and mutating instead of copying?) And since replace and all of the rest create copies, why wouldn't string.copy be valid for the same reason that Replace creates a copy instead of mutating the original like other languages? |
String.Copy has nothing to do with
Because In contrast, Worse, the problem with |
This section of the docs relies on String.Copy to avoid string interning: https://learn.microsoft.com/en-us/dotnet/csharp/how-to/compare-strings#reference-equality-and-string-interning. Should we just remove this section? cc @BillWagner |
I think so. |
Yup |
The
String.Copy
static method takes an input string instance and duplicates it, creating a new reference and copying the original contents into the new object. The method is contracted per MSDN to return a new object. (Contrast this withString.Clone
, which returns the original instance.)The only real use case for this is to create a new string instance so that the caller can use unsafe code to modify the string contents. However, as discussed in https://github.com/dotnet/coreclr/issues/14208 (the string deduping feature), it is technically illegal to mutate string instances. Furthermore, once the string deduping feature comes online, there'd no longer be any distinction between two different strings that happen to contain the same data. In that world it would make sense for
String.Copy
just to return its input argument as its return value.Given all of this, there seems to be no legitimate use for the method going forward. I propose we obsolete it both to discourage new code from calling it and to signal to existing code bases that its behavior may imminently change.
One alternative would be to change its behavior to return its input argument without also obsoleting it, but I do not believe this is a good course of action. It means that developers who rely on this method to return a duplicate instance (because they're about to perform an unsafe operation) won't ever be notified that the method is no longer safe for their particular use case.
The text was updated successfully, but these errors were encountered: