76

We have a unit test with variable that contains a very long string.

Question is how to write this in code, without having problems with line breaks or that the code is difficult to read.

In VB there is a line continue character, is there an equivilant in C#?

Shiraz Bhaiji
  • 64,065
  • 34
  • 143
  • 252
  • 1
    Create an array, then join it. – leppie Nov 03 '10 at 10:40
  • Possible duplicate of [Multiline String Literal in C#](http://stackoverflow.com/questions/1100260/multiline-string-literal-in-c-sharp) – Frank Bryce Oct 14 '15 at 21:02
  • Put the string into a resource file. – phuzi Jul 08 '19 at 08:07
  • 1
    @leppie, don't do this: then you're effectively creating multiple strings (at runtime) that need to be concatenated (at runtime). Multiple strings separated by a plus sign will be concatenated by the compiler. – bvgheluwe Oct 07 '19 at 06:54

7 Answers7

78

C# will allow you to have a string split over multiple lines, the term is called verbatim literal:

string myString = @"this is a
                    test
                   to see how long my string
                   can be



                    and it can be quite long";

If you are looking for the alternative to & _ from VB, use the + to join your lines.

Neil Knight
  • 47,437
  • 25
  • 129
  • 188
72

String Constants

Just use the + operator and break the string up into human-readable lines. The compiler will pick up that the strings are constant and concatenate them at compile time. See the MSDN C# Programming Guide here.

e.g.

const string myVeryLongString = 
    "This is the opening paragraph of my long string. " +
    "Which is split over multiple lines to improve code readability, " +
    "but is in fact, just one long string.";

IL_0003: ldstr "This is the opening paragraph of my long string. Which is split over multiple lines to improve code readability, but is in fact, just one long string."

String Variables

Note that when using string interpolation to substitute values into your string, that the $ character needs to precede each line where a substitution needs to be made:

var interpolatedString = 
    "This line has no substitutions. " +
    $" This line uses {count} widgets, and " +
    $" {CountFoos()} foos were found.";

However, this has the negative performance consequence of multiple calls to string.Format and eventual concatenation of the strings (marked with ***)

IL_002E:  ldstr       "This line has no substitutions. "
IL_0033:  ldstr       " This line uses {0} widgets, and "
IL_0038:  ldloc.0     // count
IL_0039:  box         System.Int32
IL_003E:  call        System.String.Format ***
IL_0043:  ldstr       " {0} foos were found."
IL_0048:  ldloc.1     // CountFoos
IL_0049:  callvirt    System.Func<System.Int32>.Invoke
IL_004E:  box         System.Int32
IL_0053:  call        System.String.Format ***
IL_0058:  call        System.String.Concat ***

Although you could either use $@ to provide a single string and avoid the performance issues, unless the whitespace is placed inside {} (which looks odd, IMO), this has the same issue as Neil Knight's answer, as it will include any whitespace in the line breakdowns:

var interpolatedString = $@"When breaking up strings with `@` it introduces
    <- [newLine and whitespace here!] each time I break the string.
    <- [More whitespace] {CountFoos()} foos were found.";

The injected whitespace is easy to spot:

IL_002E:  ldstr       "When breaking up strings with `@` it introduces
    <- [newLine and whitespace here!] each time I break the string.
    <- [More whitespace] {0} foos were found."

An alternative is to revert to string.Format. Here, the formatting string is a single constant as per my initial answer:

const string longFormatString = 
    "This is the opening paragraph of my long string with {0} chars. " +
    "Which is split over multiple lines to improve code readability, " +
    "but is in fact, just one long string with {1} widgets.";

And then evaluated as such:

string.Format(longFormatString, longFormatString.Length, CountWidgets());

However this can still be tricky to maintain given the potential separation between the formatting string and the substitution tokens.

Community
  • 1
  • 1
StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • 1
    In fact, Resharper will do try to do this automatically any time you push enter on an unterminated string literal. – StuartLC Jun 18 '15 at 16:14
  • 1
    This is how we do our long string literals too. Especially for helpful error messages which everyone should strive to do! – Josh P Feb 13 '17 at 21:11
  • The concatenation trick is not always viable for interpolated strings. For example, some [EntityFrameworkCore API methods](https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationaldatabasefacadeextensions.executesqlinterpolated?view=efcore-3.1) now require a `FormattableString` object as a parameter. That means it expects to receive a formattable string BEFORE it has been interpolated and converted into a normal string, but concatenation does not preserve the FormattableString objects, but instead forces the strings to be immediately interpolated/formatted. – C Perkins Apr 06 '20 at 17:37
6
@"string here
that is long you mean"

But be careful, because

@"string here
           and space before this text
     means the space is also a part of the string"

It also escapes things in the string

@"c:\\folder" // c:\\folder
@"c:\folder" // c:\folder
"c:\\folder" // c:\folder

Related

Community
  • 1
  • 1
BrunoLM
  • 97,872
  • 84
  • 296
  • 452
5

You can use verbatim literals:

const string test = @"Test
123
456
";

But the indentation of the 1st line is tricky/ugly.

H H
  • 263,252
  • 30
  • 330
  • 514
3

In his excellent answer, StuartLC cites an answer to a related question and mentions that placing newlines inside the {expression} of an interpolated string "looks odd." Most would agree, but the unpleasant source code effect can be mitigated somewhat--and without any runtime consequences--by using dedicated {expression} blocks which resolve to default(String), that is, null (and specifically not String.Empty).

The (albeit minor) point is to not mess-up or pollute your actual expression content, by instead using a dedicated token for this purpose. So if you declare a constant, for example:

const String more = null;

...then a line which might be too long to look at in source code, such as...

var s1 = $"one: {99 + 1} two: {99 + 2} three: {99 + 3} four: {99 + 4} five: {99 + 5} six: {99 + 6}";

...can be instead written like this.

var s2 = $@"{more
    }one: {99 + 1} {more
    }two: {99 + 2} {more
    }three: {99 + 3} {more
    }four: {99 + 4} {more
    }five: {99 + 5} {more
    }six: {99 + 6}";

Or perhaps you prefer a different "odd" approach to the same thing:

// elsewhere:
public const String Ξ = null;  // Unicode '\u039E', Greek 'Capital Letter Xi'

// anywhere:
var s3 = $@"{
           Ξ}one: {99 + 1} {
           Ξ}two: {99 + 2} {
           Ξ}three: {99 + 3} {
           Ξ}four: {99 + 4} {
           Ξ}five: {99 + 5} {
           Ξ}six: {99 + 6}";

Actually, it looks like we can also do it without a continuation symbol:

var s4 = $@"one: {99 + 1
            }two: {99 + 2
            }three: {99 + 3
            }four: {99 + 4
            }five: {99 + 5
            }six: {99 + 6}";

All four examples produce the same string at runtime, which in this case is all on a single line:

one: 100 two: 101 three: 102 four: 103 five: 104 six: 105

As Stuart suggested, IL performance is preserved in both these examples by not using + to concatenate strings. Although the longer format string in my new example is indeed stored in the IL, and thus your executable, the null placeholders it references are not initialized, and there are no excess concatenations or function calls at runtime. For comparison, here is the IL for the above two examples.

IL for first example

ldstr "one: {0} two: {1} three: {2} four: {3} five: {4} six: {5}"
ldc.i4.6
newarr object
dup
ldc.i4.0
ldc.i4.s 100
box int32
stelem.ref
dup
ldc.i4.1
ldc.i4.s 101
box int32
stelem.ref
dup
ldc.i4.2
ldc.i4.s 102
box int32
stelem.ref
dup
ldc.i4.3
ldc.i4.s 103
box int32
stelem.ref
dup
ldc.i4.4
ldc.i4.s 104
box int32
stelem.ref
dup
ldc.i4.5
ldc.i4.s 105
box int32
stelem.ref
call string string::Format(string, object[])

IL for second example

ldstr "{0}one: {1} {2}two: {3} {4}three: {5} {6}four: {7} {8}five: {9} {10}six: {11}"
ldc.i4.s 12
newarr object
dup
ldc.i4.1
ldc.i4.s 100
box int32
stelem.ref
dup
ldc.i4.3
ldc.i4.s 101
box int32
stelem.ref
dup
ldc.i4.5
ldc.i4.s 102
box int32
stelem.ref
dup
ldc.i4.7
ldc.i4.s 103
box int32
stelem.ref
dup
ldc.i4.s 9
ldc.i4.s 104
box int32
stelem.ref
dup
ldc.i4.s 11
ldc.i4.s 105
box int32
stelem.ref
call string string::Format(string, object[])
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
0

You must use one of the following ways:

    string s = @"loooooooooooooooooooooooong loooooong
                  long long long";

string s = "loooooooooong loooong" +
           " long long" ;
Liviu Mandras
  • 6,540
  • 2
  • 41
  • 65
  • 7
    These result in quite different strings. That is, the former get's a newline character. – Dykam Nov 03 '10 at 11:14
-2

If you declared different variables then use following simple method:

Int salary=2000;

String abc="I Love Pakistan";

Double pi=3.14;

Console.Writeline=salary+"/n"+abc+"/n"+pi;
Console.readkey();
slavoo
  • 5,798
  • 64
  • 37
  • 39