2

Given:

double price = 5.05; 
Console.Write($"{{Price = {price:C}}}");

and the desired output: {Price = $5.05}

Is there any way to associate the last two curly braces as an escaped '}' so the interpolation works as intended? As it stands, the first two are escaped(I assume?), and the output is :{Price = C}

Console.Write($"{{Price = {price:C} }}");

works as expected, but with the extra space. And I can concatenate the tail brace, which I consider a poor man's solution. Is there a colloquial rich man's solution? Thanks.

schulmaster
  • 413
  • 5
  • 16
  • in a moment of lucidity I thought "ahah!" and tried unicode escape sequences (`\u007d` for `}`), but it seems like the language designers got there before me and added CS8087 - "A '}' character may only be escaped by doubling '}}' in an interpolated string." – Marc Gravell Mar 24 '17 at 07:29
  • A similar problem exists in String.Format: http://stackoverflow.com/a/15085178/121309 , so at least it is consistent – Hans Kesting Mar 24 '17 at 07:35

5 Answers5

4

This arises because of an "oddity" in the behavior of string.Format, and our desire to have a precise 1-to-1 mapping between interpolations and inserts in the generated format string. In short, the language behavior precisely models the behavior of string.Format.

In an interpolation (the thing inside the curly braces), the expression ends either at a colon (which starts a format string), or a close curly brace. In the latter case a doubled curly brace has no special meaning because it isn't inside a literal part of the string. So three curly braces in a row would be interpreted as a close to the interpolation, followed by a literal (escaped by doubling) close curly brace. But after the colon the format string is given for that interpolation, and that format string is any string, and it is terminated by a close curly brace. If you want a close curly brace inside your format string, you simply double it up. Which is what you have unintentionally done.

CoolBots gave the best way of handling this https://stackoverflow.com/a/42993667/241658

Read the "Escaping Braces" section of https://msdn.microsoft.com/en-us/library/txafckwd(v=vs.110).aspx for an explanation of precisely this issue.

Community
  • 1
  • 1
Neal Gafter
  • 3,293
  • 20
  • 16
3

Curious workaround:

var p = price.ToString("C");
Console.Write($"{{Price = {p}}}");

For some reason, $"{{Price = {p}}}" and $"{{Price = {p:C}}}" have different associativity outcomes, which feels like a compiler bug. I'll ask around! Note that it is consistent with how string.Format applies the same rule, so it might be intentionally propagating an earlier framework oddity.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
2

You can interpolate instead of concatenate - pass it as a string literal:

double price = 5.05;
Console.Write($"{{Price = {price:C}{"}"}");
CoolBots
  • 4,770
  • 2
  • 16
  • 30
  • 5
    That's ... not very pretty tbh, but; indeed it should work – Marc Gravell Mar 24 '17 at 07:24
  • @MarcGravell, I agree, not pretty. Can be a `char` literal, which reduces the number of double quotes there, in favor of single quotes... I don't have a better idea in terms of using string interpolation syntax... `StringBuilder`? :) – CoolBots Mar 24 '17 at 07:26
  • 2
    regular concatenation would be more efficient than `StringBuilder`, as would `string.Format`; `StringBuilder` is great for unknown or large numbers of concatenations; for fixed small amounts, not so much – Marc Gravell Mar 24 '17 at 07:27
  • @CoolBots, I feel this solution is a fair compromise. TBH, I just wanted to confirm there was no meta-escape syntax of which I was unaware. – schulmaster Mar 24 '17 at 07:32
0

Well you can try with less used escape characters. Maybe \b will work as it doesn't print anything and it had no function for a really long time. Something like:

double price = 5.05;
Console.Write($"{{Price = {price:C}\b}}");

If that doesn't work for you, you can try with special UNICODE characters like U+200B or U+FEFF:

double price = 5.05;
Console.Write($"{{Price = {price:C}\x8203}}");

Escape characters: https://blogs.msdn.microsoft.com/csharpfaq/2004/03/12/what-character-escape-sequences-are-available/

UNICODE space characters: https://www.cs.tut.fi/~jkorpela/chars/spaces.html

Igor
  • 3,054
  • 1
  • 22
  • 28
  • 2
    nice, but for the record, I personally **would not** recommend using hidden characters here - it could cause lots of confusion downstream, including UI oddities; if it works for the OP, then great; but... personally I'd just rewrite the C# to use a different approach, rather than string interpolation - and get the correct output without extra bits – Marc Gravell Mar 24 '17 at 07:31
  • 1
    @MarcGravell i agree with you for another reason. String interpolation is the slowest method when concatenating strings. I have used the excellent benchmarkdotnet lib to actually test different methods of concat and found that it is by far the slowest method compared to the fastest which was string.Join(). Keep that in mind if you are in a hot code path. – Mantzas Mar 24 '17 at 08:51
0

When there are some problems with C# 6 syntax why not to use traditional string.Format() instead?

double price = 5.05;
Console.WriteLine(string.Format("{{Price = {0}}}", price.ToString("C")));
Andrey Alonzov
  • 350
  • 2
  • 9
  • 1
    huh, interesting; I *thought* this had the same problem, but curiously `Console.WriteLine(string.Format("{{Price = {0}}}", price));` has *one* associativity, and `Console.WriteLine(string.Format("{{Price = {0:C}}}", price));` **works differently**; that might actually be a compiler bug! – Marc Gravell Mar 24 '17 at 07:38