23

Consider:

string newline = "\r\n";

Console.WriteLine($"Hello without at{newline}how are you?");
Console.WriteLine($"Hello with at{@newline}how are you?");

The output of both lines is identical. The newline is always printed as a newline.

Hello without at
how are you?
Hello with at
how are you?

So when do I need the at sign inside the curly braces?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Thomas
  • 2,137
  • 1
  • 17
  • 38
  • 1
    Possible duplicate: https://stackoverflow.com/questions/6134547/what-does-the-prefix-do-on-string-literals-in-c – Keyur Ramoliya Aug 31 '18 at 08:59
  • 5
    [This one is related](https://stackoverflow.com/questions/91817/whats-the-use-meaning-of-the-character-in-variable-names-in-c) – Mong Zhu Aug 31 '18 at 09:00
  • 7
    @KeyurRamoliya: It's definitely *not* a duplicate of that. That's about verbatim string literals. This isn't one of those. It's using `@` with an *identifier*, which is typically used with keywords, e.g. `@this` as the name of the first parameter in an extension method. – Jon Skeet Aug 31 '18 at 09:00
  • @KeyurRamoliya I don't think that's a correct duplicate. HimBromBeere's answer is correct. – Sweeper Aug 31 '18 at 09:01
  • Reference for verbatim string [here](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim). – Tetsuya Yamamoto Aug 31 '18 at 09:01
  • @KeyurRamoliya: not really a duplicate because thats not a string literal, it's a variable. It's interesting and new to me that the variable itself don't need to have the `@` but that you can use this variable with or without. – Tim Schmelter Aug 31 '18 at 09:03
  • Another related thread is [@ prefix for identifiers in C#](https://stackoverflow.com/questions/33418227/). – Jeppe Stig Nielsen Aug 31 '18 at 09:46

2 Answers2

30

$"Hello { myValue }" is an interpolated string which was introduced in C#6. In your case this is equivalent to a call to String.Format("Hello {0}", myValue).

The verbatim (@) is needed when your variable has the same name as a keyword, which, as far as I know, newline is not. However the following would cause a compiler-error:

String.Format("Hello {0}", if)

whilst this won´t:

String.Format("Hello {0}", @if)

Here the verbatim tells the compiler that if is the name of a variable, not the if-keyword.

So you don´t need the verbatim in your case, because newline is not a keyword. Or in other words your code is equivalent to this:

Console.WriteLine("Hello with at{0}how are you?", @newline);

which is a valid (even though redundant) use of the verbatim.

For further information refer to the documentation about string-interpolation.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • 2
    `$` is not really a shortcut for `String.Format` it basically returns a `FormattableString` which in many ways differs from `String.Format` – mrogal.ski Aug 31 '18 at 09:09
  • @Mateusz Doesn´t `String.Format` return an `FormatableString` also? – MakePeaceGreatAgain Aug 31 '18 at 09:10
  • Not quite the same, `$` can return a `string` already processed, `FormattableString` which then can be used to process it on how you want/need it to and `IFormattable` which is the only one that really relates to `String.Format` method but it's not a shortcut, more like a long road to format. – mrogal.ski Aug 31 '18 at 09:13
  • @Mateusz Ah, I see. – MakePeaceGreatAgain Aug 31 '18 at 09:14
  • 1
    `$"Hello {myValue}"` is in fact *also* a shortcut to `string.Format` since the compiler will *only* use the `FormattableString` if the only target for the expression **is** a `FormattableString`. So the compiler will reformat this directly to a `string.Format` call. – Lasse V. Karlsen Aug 31 '18 at 09:14
  • @Mateusz Right, `$"..."` has implicit conversions to `string`, `FormattableString`, and `IFormattable`, but wrong, if the implicit conversion to `string` is used, as it was in the question, then it's just short for `string.Format`. –  Aug 31 '18 at 09:15
  • @hvd While you're right in some parts it's closer to being a shortcut for `ToString` or `ToString(IFormatProvider)` than it is to `String.Format`. Simply because an interface `IFormattable` ensures this is the method called when converting to `string`. This method might use `String.Format` behind but, like with `bool` eaxmple can differ. – mrogal.ski Aug 31 '18 at 09:22
  • Exactly. The expression `$"..."` has a type by itself which is `string` (as seen with `var example = $"...";`), and so the "normal" case is that it is equivalent to `string.Format`. However, the expression `$"..."` _also_ has the implicit conversion to `FormattableString` (and the interface `IFormattable` which `FormattableString` implements), and when that implicit conversion is taking place, `$"..."` will not go through `string.Format` (which would lose information about the interpolated arguments) but will instead produce a "rich" object with more information than a plain `string`. – Jeppe Stig Nielsen Aug 31 '18 at 09:22
  • With more details, the `$"..."` will be a call to `string.Format` in the usual case; but will be a call to another static method with the same signature, namely `System.Runtime.CompilerServices.FormattableStringFactory.Create`, in the case where the implicit conversion I mentioned in my first comment, is needed. This can be seen in §7.6.2 in the version of the C# Spec that I have with me here. – Jeppe Stig Nielsen Aug 31 '18 at 09:30
  • @Mateusz I do not understand your previous comment. How would `bool` change anything? Given `bool b;`, the only way I can see it used in the context of an interpolated string is in e.g. `var x = $"{b}";`, which is equivalent to `var x = string.Format("{0}", b);`. –  Aug 31 '18 at 10:28
  • @hvd `Boolean.ToString` method uses simple if statement and returns one of the two constant values which does not use `String.Format`. That was the example I was about. `IFormattable` and every evaluation of it, uses `ToString` method to produce a `string` which not necessarily has to use `String.Format` behind it. – mrogal.ski Aug 31 '18 at 10:45
  • @Mateusz That doesn't change anything: sure, `bool.ToString` doesn't call `string.Format`, but `$"{b}"` calls `string.Format` which in turn calls `bool.ToString`. You seem to be assuming that `$"{b}"` is optimised by the compiler to just `b.ToString()`, but no such optimisation exists. You can easily try it: compile `class C { string F() => $"{true}"; }` with your C# compiler and disassemble the result. You'll literally see a `string.Format` invocation there. –  Aug 31 '18 at 10:49
  • @hvd I'm not expecting `$"{b}"` to be translated to `b.ToString()`. I'm saying that `IFormattable` as well as `FormattableString` do not use `String.Format` under the hood. They use `ToString` or `ToString(IFormatProvider)`. Every expression that has to evaluate to some datatype is optimized by the compiler, that's why sometimes you'll see `String.Format` and sometimes `IFormatter.ToString`. – mrogal.ski Aug 31 '18 at 12:02
  • @Mateusz Okay. That's completely irrelevant here then, as neither `IFormattable` nor `FormattableString` is used for the `$"..."`, not even implicitly. The translation of `$"..."` to `string.Format(...)` here isn't an optimisation, it's a hard requirement spelt out in the language spec. –  Aug 31 '18 at 12:05
  • Ew! This is a feature that should never be used in practice. (It shouldn't even *exist* in my opinion.) *Don't name your variables after keywords.* – jpmc26 Aug 31 '18 at 15:55
  • 2
    This answer is basically correct but it makes a small overstatement. The `@` does not say *this is the name of a variable*, because the name might not resolve to a *variable*. It might resolve to a constant, method, property, class, struct, type parameter, or event, none of which are *variables*. Rather, it says that this *identifier* is *not to be treated as a keyword*. – Eric Lippert Aug 31 '18 at 19:50
  • @jpmc26: Your comment contains a logical fallacy. Yes, it is a bad idea to deliberately name your local variables the same as keywords. **That is not why the feature was added to the language**, so concluding that it is a bad feature *because it lets you do something bad* is fallacious reasoning. **There are many good reasons for this feature**. Knowing that the feature is explicitly *not* designed to facilitate writing bad code, can you now think of some reasons why it is a good feature? (Hint: C# was designed with *versionability* in mind at all times; what does that tell you?) – Eric Lippert Aug 31 '18 at 19:52
  • @EricLippert I thought that through. If you add a keyword to the language, renaming the variable isn't harder than going back and prepending this character to the word. Both involve finding all references and making a modification. In fact, I suspect a rename is *easier* with VS tools. (You may need to do it in an older version before upgrading, but there's no tool in any version I'm aware of to do this prepending.) – jpmc26 Aug 31 '18 at 20:04
  • @jpmc26: You're starting to reason like a compiler developer. Go farther. You've correctly deduced that *editing the code one way to fix a breaking change imposed by the compiler team is neither easier nor harder than editing the code a different way*. Therefore the feature is probably not to make the editing cost given a breaking change lower. Now ask yourself two questions. (1) has the C# compiler team *ever* added a keyword that would create such a breaking change? If no, then what does that tell you about your logic so far? And (2) under what circumstances is making any edit expensive? – Eric Lippert Aug 31 '18 at 20:08
  • @EricLippert Editing is expensive at scale. If you're driving at dependency propagation, leveraging this feature is *still* a bad idea. A better solution would be to immediately create a replacement that's not a keyword (and modify the old one to be a derived property until it can be removed), continue compiling with the older framework version for a while, and deprecate the old name while everyone switches over to the new one. Devs of dependent software would still need to make a similar amount of changes to their code with that set up (no changes with old frameworks, minor edit in new). – jpmc26 Aug 31 '18 at 20:22
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/179208/discussion-between-jpmc26-and-eric-lippert). – jpmc26 Aug 31 '18 at 20:29
  • 1
    @jpmc26: I'm not driving at that. I'm driving at two scenarios, (1) a third party makes a library that you depend on but do not control where one of its entrypoints is a C# keyword. You need some way to call it! And (2) code generated parametrically by a machine from user input is code which needs to be robust in the face of its users accidentally putting C# keywords in the schemas. – Eric Lippert Aug 31 '18 at 20:31
  • 1
    @EricLippert In 1, if it's not well maintained enough to keep up with rare *keyword* additions, you don't want to be using it anyway; it's almost certainly suffering from other long standing problems. So it still just facilitates using garbage. In 2, they should be [validating the input before generating](https://stackoverflow.com/a/44728184/1394393). This would future proof the code base against any new keyword additions. Barring that, they'd have to pervasively put `@` on *every single identifier* for it to help with future proofing. Otherwise, it'd break with the update anyway. – jpmc26 Sep 01 '18 at 05:17
  • 1
    It is also worth to mention about interoperability between different languages. Keyword in one language is not necessarily the keyword in the second. – Karol Sep 01 '18 at 12:11
15

It's a redundant verbatim prefix. From the C# specification:

The prefix "@" enables the use of keywords as identifiers, which is useful when interfacing with other programming languages. The character @ is not actually part of the identifier, so the identifier might be seen in other languages as a normal identifier, without the prefix. An identifier with an @ prefix is called a verbatim identifier. Use of the @ prefix for identifiers that are not keywords is permitted, but strongly discouraged as a matter of style.

A use case would be if you would want to write a function like this:

private void print(string @string) => Console.WriteLine(@string);

Normally you would not be able to name an identifier string because it is a reserved keyword. The @ prefix enables you to do so.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ChristianMurschall
  • 1,621
  • 15
  • 26
  • 4
    Important part is _"The character @ is not actually part of the identifier"_ because that explains why it compiles even if the variable doesn't have it. – Tim Schmelter Aug 31 '18 at 09:08
  • 4
    Another part that I find important is _"Use of the @ prefix for identifiers that are not keywords is permitted, but strongly discouraged as a matter of style."_ So `@newline` is tolerated but misleading/confusing. – Jeppe Stig Nielsen Aug 31 '18 at 09:40
  • I note that your example does not match the text you've quoted from the specification, and that this is a very poor use case. The purpose of this feature is not to allow you to make a *formal parameter* called `@string` because that would be a bizarre thing to do. The purpose of the feature is to allow you to, say, extend a third-party class written in a non-C# language that has a virtual member called `string`. You need to be able to say `class D : ThirdParty { public override void @string() { ... } }`. That would be a by-design use case. – Eric Lippert Aug 31 '18 at 21:13
  • @EricLippert It might be bizarre, but this [example](https://stackoverflow.com/a/21797430/4634044) is what people use it for. (just stumbled across this) – ChristianMurschall Sep 20 '18 at 14:40
  • Though I agree that is a use case it is not a *motivating* case. That is, if the feature did not exist, we would not use this scenario as motivation to create the feature. Anyone who uses @this could just as easily use _this, so there is no compelling benefit. – Eric Lippert Sep 20 '18 at 14:44
  • Also obviously that was certainly not a motivating scenario since extension methods were added in v3. – Eric Lippert Sep 20 '18 at 14:45