Declaring long strings that use string interpolation in C# 6
Asked Answered
G

4

31

I usually wrap long strings by concatenating them:

Log.Debug("I am a long string. So long that I must " +
    "be on multiple lines to be feasible.");

This is perfectly efficient, since the compiler handles concatenation of string literals. I also consider it the cleanest way to handle this problem (the options are weighed here).

This approach worked well with String.Format:

Log.Debug(String.Format("Must resize {0} x {1} image " +
    "to {2} x {3} for reasons.", image.Width, image.Height,
    resizedImage.Width, resizedImage.Height));

However, I now wish to never use String.Format again in these situations, since C# 6's string interpolation is much more readable. My concern is that I no longer have an efficient, yet clean way to format long strings.

My question is if the compiler can somehow optimize something like

Log.Debug($"Must resize {image.Width} x {image.Height} image " +
    $"to {resizedImage.Width} x {resizedImage.Height} for reasons.");

into the above String.Format equivalent or if there's an alternative approach that I can use that won't be less efficient (due to the unnecessary concatenation) while also keeping my code cleanly structured (as per the points raised in the link above).

Gersham answered 31/8, 2015 at 22:47 Comment(11)
I don't see any reason why it would be different than using String.FormatSamara
Well, I did test it in LINQPad, and it's definitely performing a concatenation when using C# 6's string interpolation (which does not occur when using String.Format explicitly). I'm mostly asking in hopes that there may be an alternative approach, compiler option, etc.Gersham
I'm surprised the format string isn't considered a compile-time constantDetect
@JeroenVannevel How can it be a compile time constant if the values being concatenated are set at runtime?Samara
@Magnus: the format string (aka: the first argument in a string.Format() call) is not set at runtime. At least: it's created a compile-time.Detect
It might well be that each string-interpolated string is transformed to a call to string.Format itself (indeed emitting format strings like "Must resize {0} x {1} image ", which are definitely treated as compile-time constant!), but now we are concatenating results of calls to string.Format, whereas the 2 constant format strings ended up as parameters - where they obviously are not concatenated by the compilerKinnie
@olydis, this is correct, as per the IL code.Gersham
You could use a verbatim interpolated string instead ($@"...")Geodynamics
Although verbatim interpolated strings have the formatting issues mentioned in point #2 of the linked answer. Namely that they wreck havoc on your indentation. They're worse than nothing, IMO (at least if I do nothing, some editors can "smart wrap").Gersham
I think the whole "strings are immutable, don't concatenate them" wisdom has gotten out of control. As with any "rule" in computing there are plenty of exceptions to this one. It really only becomes problematic when trying to do real-time (or near real-time) processing (which you probably shouldn't do in C# anyway due to the unpredictable nature of GC) or if you're doing dozens (more realistically hundreds) of concatenations. Concatenating five strings will get lost in the noise of everything else that is going on. I wouldn't worry about it and err on the side of readable code.Embrocate
For anyone wondering, the current workaround I have is just to not break up strings on multiple lines. I'm just letting Visual Studio do the line wrapping on its own. I'm rather iffy on this because most tools to display code will not line wrap or do a very bad job at it, which causes problems with review tools and the like. Also, it has introduced an inconsistency with how to handle line wrapping, but I suspect I'll just convert all my future code to utilize IDE linewrapping.Gersham
S
16

This program:

var name = "Bobby Tables";
var age = 8;
String msg = $"I'm {name} and" +
    $" I'm {age} years old";

is compiled as if you had written:

var name = "Bobby Tables";
var age = 8;
String msg = String.Concat(String.Format("I'm {0} and", name),
    String.Format(" I'm {0} years old", age));

You see the difficulty in getting rid of the Concat - the compiler has re-written our interpolation literals to use the indexed formatters that String.Format expects, but each string has to number its parameters from 0. Naively concatenating them would cause them both to insert name. To get this to work out correctly, there would have to be state maintained between invocations of the $ parser so that the second string is reformatted as " I'm {1} years old". Alternatively, the compiler could try to apply the same kind of analysis it does for concatenation of string literals. I think this would be a legal optimization even though string interpolation can have side effects, but I wouldn't be surprised if it turned out there was a corner case under which interpolated string concatenation changed program behavior. Neither sounds impossible, especially given the logic is already there to detect a similar condition for string literals, but I can see why this feature didn't make it into the first release.

I would write the code in the way that you feel is cleanest and most readable, and not worry about micro-inefficiencies unless they prove to be a problem. The old saying about code being primarily for humans to understand holds here.

Stickpin answered 6/11, 2015 at 1:1 Comment(1)
This seems like it's only a problem because the compiler doesn't realize that $"{foo}" + $"{bar}" is the same as $"{foo}{bar}". If it can remove the concatenation first (at compile time), then this optimization seems to make sense. Although yeah, it doesn't seem currently possible (requires compiler support). I'm using temporary workaround of just not breaking my lines up and letting VS do soft line wrapping.Gersham
J
6

Maybe it would be not as readable as with + but by all means, it is possible. You just have to break line between { and }:

Log.Debug($@"Must resize {image.Width} x {image.Height} image to {
    resizedImage.Width} x {resizedImage.Height} for reasons.");

SO's colouring script does not handle this syntax too well but C# compiler does ;-)

Jevon answered 16/1, 2018 at 16:40 Comment(1)
That's an interesting approach that I hadn't thought of. I like that since we're in between the braces we can play with whitespace without affecting the string.Nichols
L
1

In the specialized case of using this string in HTML (or parsing with whatever parser where multiple whitespaces does not matter), I could recommend you to use @$"" strings (verbatim interpolated string) eg.:

$@"some veeeeeeeeeeery long string {foo} 
whatever {bar}"
Labret answered 21/3, 2017 at 13:56 Comment(1)
I like that verbatim strings can be split across lines but having to align it all the way to the left kills me so I never end up doing it.Nichols
E
-4

In c# 6.0:

var planetName = "Bob";
var myName = "Ford"; 
var formattedStr = $"Hello planet {planetName}, my name is {myName}!";
// formattedStr should be "Hello planet Bob, my name is Ford!"

Then concatenate with stringbuilder:

StringBuilder stringBuilder = new StringBuilder();

stringBuilder.Append(formattedStr);
// Then add the strings you need 

Append more strings to stringbuilder.....

Egocentric answered 21/9, 2015 at 7:0 Comment(3)
I don't see how that's any cleaner than just using operator +, which will use a single Concat call in the IL (just as efficient as StringBuilder, but more readable). Further, your code still has the previously mentioned problem of an unnecessary concatenation and using multiple String.Format calls, which there wouldn't be if we used a single string.Gersham
StringBuilder gives better performance with serveral strings compared to "+".Egocentric
Not necessarily in the case here, where the strings are known at compile time. It gets converted into a single String.Concat call. The main reason to use StringBuilder is to avoid the unnecessary creation of objects that would occur if you had repetitively concatenated (particularly if concatenating in a loop). But here we're talking about a typically fairly small number of strings that just happens to be too long to cleanly line wrap. Also see this question.Gersham

© 2022 - 2024 — McMap. All rights reserved.