.NET Tools
Essential productivity kit for .NET and game developers
Raw Strings, UTF-8 Strings, and Multiline Interpolations – Using C# 11 in Rider and ReSharper
The .NET 7 SDK arrived a few months ago, with many .NET developers looking forward to this release and the brand-new C# language features that come along with it. If you haven’t put your fingers on it yet, all you need is:
- Download the latest .NET SDK
- Update your
global.json
if you have one - Update the
TargetFramework
in your project tonet7.0
(only for some features) - Update the
LangVersion
property in your project to11.0
orpreview
In this series, we will dive into the most interesting features that are coming with C# 11 and show how we updated ReSharper and Rider to support you in applying them to your codebase with ease:
- List and Span Pattern Matching
- Raw Strings, UTF-8 Strings, and Multiline Interpolations
- Required Keyword, Checked Operators, nameof Operator Scope
- Static Interface Members, Generic Attributes, Auto-Default Structs
This second post will walk you through raw strings, multiline string interpolation, and the new UTF-8 string literals.
Raw Strings
Embedding text and code fragments into string literals becomes difficult once they contain characters that need to be escaped, for example, double-quotes for HTML tag attributes or backslashes in Windows paths. ReSharper and Rider got the Smart Paste feature onboard, which helps you to escape a string after it is pasted. Though, there is no particular help when writing strings from scratch.
Verbatim strings reduce the friction of escaping to some degree, e.g., you don’t need to escape backslashes, and you can define multiline fragments. However, the following JSON example illustrates that there are still tradeoffs to deal with, for example, the escaping of double-quotes and an inevitable misalignment of the fragment in itself and related to the surrounding code:
class Class { public void Method() { // some code var json = $@"{{ ""name"": ""Lucy"", ""age"": 32 }}"; // more code } }
With raw strings from C# 11, these are problems of the past. Raw strings are delimited by 3 double-quotes at the start and end of the literal. Inside these strings, you can freely use up to 2 consecutive double-quotes without any escaping, as in the following JSON example:
var json = """{"name":"Lucy","age":32}""";
But wait. What should you do if your text fragment contains more than 2 double-quotes in a sequence? Well, the C# language team has a simple solution: just add another double-quote to delimit the raw string! Because – to be more precise – a raw string is delimited by at least 3 double-quotes. For the sake of the argument, let’s consider for a moment that JSON properties are enclosed with 3 double-quotes:
var json = """"{"""name""":"""Lucy""","""age""":32}"""";
Pretty neat, huh? This game can be continued with whatever chain of double-quotes you need. You just need to add more double-quotes to the start and end. To put it with Mads Torgersen’s words, “you can always win”.
Raw strings also elegantly solve the alignment problem of multiline fragments. Both the start and end delimiters can be put onto their own line to not obstruct the readability of the fragment and without introducing line breaks in the resulting string (as opposed to verbatim strings). In addition, the closing delimiter indicates how much indentation is automatically removed from the string. That means that the fragment can be properly aligned according to the surrounding code and doesn’t stick to the very left:
var json = """ { "name": "Lucy", "age": 32 } """;
As expected, raw strings can also be combined with string interpolation. And indeed, in order to use braces {}
unescaped (i.e., without opening an interpolation hole), the same game as for double-quotes applies: increase the amount of leading $
to the number of {
/}
you want to use without escaping them in the string:
var json = $$"""This is pretty {{{1030 + 307}}}!"""; // This is pretty {1337}
ReSharper and Rider both support raw strings in various forms. We’ve added a new To Raw String context action, which allows you to convert all verbatim strings in your solution in one go:
It’s easy to miss the alignment between the content and the closing delimiter, so ReSharper and Rider will let you know ahead of compilation when there is an issue:
Of course, we’ve also updated our existing features to properly work with raw strings, including language injections and context actions to insert interpolations. In this example, note that the correct number of $
signs is added to the front of the raw string:
Speaking of string interpolation…
Multiline String Interpolation
Sometimes, our expressions in interpolated string holes get a little more complex. Up until C# 10, we had two choices. Either we keep them as one-liners and accept the sub-optimal readability …
Console.WriteLine($"We welcome you to our {allEvents.Where(x => x.Date >= DateTime.Today).OrderBy(x => x.Name).Select(x => x.Name).JoinComma()} events.");
… or we introduce another variable, but with the downside that it might bloat our method:
var events = allEvents.Where(x => x.Date >= DateTime.Today).OrderBy(x => x.Name).Select(x => x.Name); // more variables Console.WriteLine($"We welcome you to our {events.JoinComma()} events."); // more console output
With C# 11, you can freely span your interpolation holes across multiple lines and align/format them to your preferences for better readability. Maybe you still prefer to extract some code, but it’s really nice to have the choice and be able to write the above like this instead:
Console.WriteLine($"We welcome you to our { allEvents.Where(x => x.Date => DateTime.Today) .OrderBy(x => x.Name) .Select(x => x.Name) .JoinComma() } events.");
ReSharper and Rider are ready to help with multiline interpolations. All the string features that you love should work, including the power-user action for Extend Selection and Shrink Selection:
UTF-8 Strings
May it be for the web or plenty of other stacks, UTF-8 is the predominant encoding that developers use. Previously, implementing network communication in .NET based on UTF-8 has often been a choice between efficient but verbose or concise but slow:
// Verbose + startup allocation + efficient static readonly byte[] s_authWithTrailingSpace = Encoding.UTF8.GetBytes("AUTH "); WriteBytes(s_authWithTrailingSpace); // Concise + inefficient WriteBytes(Encoding.UTF8.GetBytes("AUTH "));
With C# 11, you can mark your strings as UTF-8 with the u8
suffix, which will make the compiler do the heavy lifting of turning it into an efficient ReadOnlySpan<byte>
:
// C# WriteBytes("HTTP/1.1"u8); // Decompiled WriteBytes(new ReadOnlySpan<byte>((void*) &<PrivateImplementationDetails>.93BA0E8A9E08C993FC2BAC1F290D3C6CB33E2BB1E6825E8DDF78B62BCA2D917A, 8).Length);
ReSharper and Rider understand the new u8
literal suffix and many string-related features will work just as you know them, including splitting strings via Enter or converting them to verbatim/raw strings:
As with many new language features, the crucial part to success is how quickly you can migrate your codebase to using them. As you might have guessed, we also ship a quick-fix to convert to the leaner UTF-8 string form:
Conclusion
With raw strings, you rarely have to worry about escaping again, and your code will look pretty even in the multiline form. UTF-8 strings are a nice addition that can make high-performance code more readable.
So how about you? Which of the two will you use more often in your codebase? Let us know in the comments! Especially when you have more ideas for additional features in ReSharper and Rider!
Stay tuned for the next blog post, where we will take a look at everything related to keywords and operators!