Take advantage of String.Create to create strings with no allocation overhead and improve the performance of your .NET 6 applications. Credit: CalypsoArt / Getty String handling is one of the most performance-critical areas in any application. Because strings are immutable, you can very easily accumulate many string objects very quickly, resulting in memory resource allocations that will impact application performance adversely. When you add strings or extract substrings from a string, new string instances are created. Same goes when you perform operations such as string concatenation, which creates new string objects rather than reusing existing ones. We’ve seen how we can take advantage of the StringBuilder class when concatenating strings to reduce the number of string instances created and also reduce the allocations. Continuing our discussion on working with strings efficiently, in this article we’ll look at how we can use the String.Create method to create strings with no resource overhead whatsoever. While string compression is a great technique for reducing resource consumption generally, String.Create is another technique you can use to handle strings efficiently—but only in certain circumstances, which we’ll discuss. To work with the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here. Create a console application project in Visual Studio 2022 First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2022 is installed in your system, follow the steps outlined below to create a new .NET Core console application project. Launch the Visual Studio IDE. Click on “Create a new project.” In the “Create a new project” window, select “Console App” from the list of templates displayed. Click Next. In the “Configure your new project” window shown next, specify the name and location for the new project. In the “Additional Information” window, select .NET 6.0 as the runtime and click Next Click Create. We’ll use this .NET 6 console application project to work with strings in the sections below. Span and Memory Span and Memory are structs that have been added in the newer versions of .NET and that help to minimize allocations. They work as a facade over a string, array, or any contiguous block of memory. They also have read-only counterparts. The read-only counterpart of the Span struct is ReadOnlySpan, and the read-only counterpart of Memory is ReadOnlyMemory. The String.Create method in C# The String.Create method was added in the recent versions of C#. Here is how the Create method of the String class is declared: public static string Create<TState> (int length, TState state, System.Buffers.SpanAction<char,TState> action); The String.Create method requires the following: The length of the string to create The data (i.e., the state) A lambda function that can transform the state into a string The String.Create method allocates a chunk of memory on the heap to store a sequence of characters. The first parameter of this method is the length of the final string. The second parameter is the state required to build the string object. The third and last parameter is a delegate that should work on the data in the allocated heap and generate the final string object. When you call the String.Create method, it creates a new string that has a pre-defined size determined by the value of your length argument. Note that this is the only heap allocation that will occur when you’re using the String.Create method. Since the Create method is a member of the String class, it can access the Span instance that represents the internal character data of the new string instance. The Span itself is a pointer that resides on the stack but is capable of working on the heap memory. The action lambda performs the heavy lifting of populating the string that is eventually returned to you. In other words, once the execution of the lambda function is complete, the String.Create method returns a reference to the new string instance it has created. When to use the String.Create method String.Create has a few specific use cases. First, you should use String.Create only in performance-critical paths. Second, you should use String.Create only when you want to build a string object when the size and format of the string are known to you. As an example, let’s say you want to log correlation Id to a log file with every method call for each and every request. You might take advantage of String.Create to build such string instances efficiently. You might also use String.Create for performance-sensitive concatenations and formatting complex strings. Using the String.Create method Here is a simple example of using the String.Create method: char[] buffer = { 'a', 'e', 'i', 'o', 'u' }; string result = string.Create(buffer.Length, buffer, (c, b) => { for (int i = 0; i < c.Length; i++) c[i] = b[i]; }); Below is another example that illustrates how you can use String.Create to generate correlation Ids. Enter the following code in the Program.cs file of the console application project we created earlier. private static readonly char[] charactersToEncode = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray(); private static string GetCorrelationId(long id) { return string.Create(10, id, (buffer, value) => { char[] characters = charactersToEncode; buffer[9] = characters[(value >> 5) & 31]; buffer[8] = characters[(value >> 10) & 31]; buffer[7] = characters[(value >> 15) & 31]; buffer[6] = characters[(value >> 20) & 31]; buffer[5] = characters[(value >> 25) & 31]; buffer[4] = characters[(value >> 30) & 31]; buffer[3] = characters[(value >> 35) & 31]; buffer[2] = characters[(value >> 40) & 31]; buffer[1] = characters[(value >> 45) & 31]; buffer[0] = characters[(value >> 50) & 31]; }); } To get a new correlation Id, you can call the GetCorrelationId method from the Main method as shown below: static async Task Main(string[] args) { Console.WriteLine(GetCorrelationId(DateTime.UtcNow.Ticks)); Console.ReadKey(); } String.Create limitations and best practices When using String.Create, you should first of all keep its limitations in mind. You should know the size of the string you want to create in advance, which will require knowing the length of the state objects that the final string will be composed of. There are also two best practices you should adhere to when working with the String.Create method. First, it’s wise to benchmark the performance of your application to ensure that using String.Create actually yields better results. Second, if you’re using multiple objects for the state, be sure to take advantage of ValueTuples. Finally, note that String.Create might not be a good choice in certain scenarios. You should not use String.Create when readability or culture is important for your application or your development team. So, whether you should use String.Create or not depends on the trade-offs between its downsides and performance benefits. My advice is, benchmark your code, see the results, and then decide. I’ll write more on writing high performance code in future posts here. Related content news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks news Akka distributed computing platform adds Java SDK Akka enables development of applications that are primarily event-driven, deployable on Akka’s serverless platform or on AWS, Azure, or GCP cloud instances. By Paul Krill Nov 18, 2024 2 mins Java Scala Serverless Computing news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks how-to How to use DispatchProxy for AOP in .NET Core Take advantage of the DispatchProxy class in C# to implement aspect-oriented programming by creating proxies that dynamically intercept method calls. By Joydip Kanjilal Nov 14, 2024 7 mins Microsoft .NET C# Development Libraries and Frameworks Resources Videos