Does anyone know how this method is intended to be used? The documentation is somewhat 'light'!
public static string Create<TState> (int length, TState state, System.Buffers.SpanAction<char,TState> action);
https://docs.microsoft.com/en-us/dotnet/api/system.string.create?view=netcore-2.2
The
String.Create()
method needs three things:length
of the string. You must know this in advance, because the method needs it to safely create an internal fixed-length buffer for theSpan<char>
instance used to construct the final string.state
) which will become your string. For example, you might have an array buffer (of, say, ascii integers received over the network), but it could be anything. This is the raw data that will be transformed into the final string. There is an example buried deep in this MSDN article that even uses aRandom
instance. I've also seen an incomplete example used to create a base-64 encoded hash value (fixed length) of bitmap images (variable sizedstate
input), but sadly I can't find it again.action
lambda function that transformsstate
into the characters for the final string. TheCreate()
method will call this function, passing the internalSpan<char>
it created for the string and yourstate
data as the arguments.For a very simple example, we can
Create()
a string from an array of characters like this:Of course, the basic
string(char[])
constructor would also work here, but that shows what a correct function might look like. Or we can map an array of asciiint
values to a new string like this:The function exists because there are some significant potential performance wins for this technique over traditional methods. For example, rather than reading a Stream into a buffer, you could pass the Stream object directly to
String.Create()
(assuming you know the final length). This avoids needing to allocate a separate buffer and avoids one round of copying values (stream=>buffer=>string becomes just stream=>string).What happens when you call
string.Create()
is the function constructs a new string that already has the size determined by yourlength
argument. This is one (and only one) heap allocation. BecauseCreate()
is a member of the string type, it has access to private string data for this new object you and I normally can't see. It now uses this access to create an internalSpan<char>
instance pointed at the new string's internal character data.This
Span<char>
lives on the stack, but acts on the heap memory from the new string... there is no additional allocation, and it's completely out of scope as soon as theCreate()
function returns, so everything is legal and safe. And because it's basically a pointer-with-benefits, there's virtually no risk of overflowing the stack unless you've done something else horribly wrong.Now
Create()
calls youraction
function to do the heavy lifting of populating the string. Youraction
lambda can write into theSpan<char>
... for the duration of your lamdba's execution, strings are less-immutable than you may have heard!When the
action
lamdba is finshed,Create()
can return the new, ready-to-use, string reference. Everything is good: we minimized heap allocations, preserved type safety and memory safety; theSpan<char>
is no longer accessible anywhere, and as a stack value is already destroyed. We also minimized unnecessary copying between buffers, depending on youraction
implementation.