42

So a bit of a weird question I was having trouble coming up with the search terms for. If I have a multi-line string literal in my program, is there anyway to keep the indentation of my code consistent without adding unwanted white space to my string literal?

Ex:

if (true)
{
    if (!false)
    {
        //Some indented code;
        stringLiteral = string.format(
@"This is a really long string literal
I don't want it to have whitespace at 
the beginning of each line, so I have
to break the indentation of my program
I also have vars here 
{0}
{1}
{2}",
var1, var2, var3);
    }
}

It's probably just my OCD talking, but is there anyway to maintain the indentation of my program without adding unwanted whitespace to the string, or having to build it line by line (the real string is a super long string.format that is 20~ lines with 12 variables inside)?

Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138
  • You could add pseudocode that demonstrates the desired indentation and the undesired white-spaces. – Tim Schmelter Jun 25 '13 at 21:26
  • 1
    @TimSchmelter I would just like to indent the string to match the indentation of the rest of the code, however if I were to do that it would add tabs/spaces to the string literal. I want the code to be functionally the same, but with the string tabbed to match the indentation of the rest of the program. – Kevin DiTraglia Jun 25 '13 at 21:27
  • Maybe with a string that long you could consider moving it to a separate static class/member or some resource. – Chris Sinclair Jun 25 '13 at 21:29
  • @ChrisSinclair In my program it isn't actually a static string, but built using several variables at run-time using a giant string.format() on a big string literal. I'll edit the question to make that more clear. – Kevin DiTraglia Jun 25 '13 at 21:31
  • KEvin, I actually wrote a personal little library called "BetterStringLiterals". Do you want it? It allows you to do exactly this. – Brent Rittenhouse Oct 23 '18 at 22:43
  • Please see (and upvote) my suggestion for Visual Studio: [Indent multi-line verbatim strings](https://developercommunity.visualstudio.com/idea/602807/indent-multi-line-verbatim-strings.html). – Olivier Jacot-Descombes Aug 11 '19 at 17:49

10 Answers10

12

I would abstract it to a separate static class or resource completely:

public static class MyStringResources
{
    public static readonly string StringLiteral = 
@"This {0} a really long string literal
I don't want {1} to have {2} at 
the beginning of each line, so I have
to break the indentation  of my program";

}

With usage like:

stringLiteral = String.Format(MyStringResources.StringLiteral, var1, var2, var3);

Even better, this way you can have a nice function that requires the number of expected variables:

public static class MyStringLiteralBuilder
{
    private static readonly string StringLiteral = 
@"This {0} a really long string literal
I don't want {1} to have {2} at 
the beginning of each line, so I have
to break the indentation  of my program";

    public static string Build(object var1, object var2, object var3)
    {
        return String.Format(MyStringResources.StringLiteral, var1, var2, var3);
    }
}

Then you can't miss variables accidentally (and possibly even strongly type them to numbers, booleans, etc.)

stringLiteral = MyStringLiteralBuilder.Build(var1, var2, var3);
stringLiteral = MyStringLiteralBuilder.Build(var1, var2); //compiler error!

Of course at this point, you can do pretty much whatever you want with these builders. Make a new builder for each special big "stringLiteral" you have in your program. Maybe instead of having them static they can be instances that you can get/set the key properties, then you can give them nice names too:

public class InfoCardSummary
{
    public string Name { get; set; }
    public double Age { get; set; }
    public string Occupation { get; set; }

    private static readonly string FormattingString = 
@"This person named {0} is a pretty
sweet programmer. Even though they're only
{1}, Acme company is thinking of hiring
them as a {2}.";

    public string Output()
    {
        return String.Format(FormattingString, Name, Age, Occupation);
    }
}

var info = new InfoCardSummary { Name = "Kevin DiTraglia", Age = 900, Occupation = "Professional Kite Flier" };
output = info.Output();
Chris Sinclair
  • 22,858
  • 3
  • 52
  • 93
12

In my case it was acceptable to make an extension method like this:

class Program
{
    static void Main(string[] args)
    {
        var code = $@"
            public static loremIpsum(): lorem {{
                return {{
                    lorem: function() {{
                        return 'foo'
                    }},
                    ipsum: function() {{
                        return 'bar'
                    }},
                    dolor: function() {{
                        return 'buzz'
                    }}
                }}
            }}".AutoTrim();

        Console.WriteLine(code);
    }
}

static class Extensions
{
    public static string AutoTrim(this string code)
    {
        string newline = Environment.NewLine;
        var trimLen = code
            .Split(newline)
            .Skip(1)
            .Min(s => s.Length - s.TrimStart().Length);

        return string.Join(newline,
            code
            .Split(newline)
            .Select(line => line.Substring(Math.Min(line.Length, trimLen))));
    }
}

enter image description here

Yegor
  • 2,514
  • 2
  • 19
  • 27
  • I don't think you can split on a string by itself. I declared `string[] newline = new[]{ Environment.NewLine }` first with the second arg to split being `StringSplitOptions.None`, then in the String.Join, I used `Environment.NewLine` as the first arg. – General Grievance Jan 18 '21 at 18:44
7

Update - Raw String Literals - C#11 / .NET7 (Nov 2022)

This will be made much easier to handle with the release of raw string literals in C#11

Raw string literals are a new format for string literals. Raw string literals can contain arbitrary text, including whitespace, new lines, embedded quotes, and other special characters without requiring escape sequences. A raw string literal starts with at least three double-quote (""") characters. It ends with the same number of double-quote characters.

Any whitespace to the left of the closing double quotes will be removed from the string literal.

So the following three examples create identical strings:

public class Program
{
    public static void Main()
    {

        var message1 = 
@"First Line
Second Line";

        var message2 = 
            """
            First Line
            Second Line
            """;

        var message3 = """
                       First Line
                       Second Line
                       """;
    }
}

Demo in .NET Fiddle / SharpLab

Socko
  • 665
  • 2
  • 9
  • 2
    It seems like language version is tied to .NET version, so C# 11 is .NET 7 - for reference. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version – Filip Skakun Jan 26 '23 at 00:14
  • The raw string literals doc also says that "The newlines following the opening quote and preceding the closing quote aren't included in the final content." Therefore the equivalent `@""` string would need its leading and trailing newlines removed to match. – aaaantoine Aug 08 '23 at 13:02
  • 1
    @aaaantoine, thanks fixed and included a runnable fiddle – Socko Aug 16 '23 at 17:21
  • @FilipSkakun. thanks, also added .net version number answer – Socko Aug 16 '23 at 17:22
3
if (true)
{
    if (!false)
    {
        //Some indented code;
        stringLiteral = string.format(
                    "This is a really long string literal. " +
                    "I don't want it to have whitespace at " +
                    "the beginning of each line, so I have " +
                    "to break the indentation of my program " +
                    "I also have vars here: " +
                    "{0} " +
                    "{1} " +
                    "{2}",
                  var1, var2, var3);
          // OR, with lineskips:
        stringLiteral = string.format(
                    "This is a really long string literal\r\n" +
                    "I don't want it to have whitespace at \r\n" +
                    "the beginning of each line, so I have\r\n" +
                    "to break the indentation of my program\r\n"
                    "I also have vars here\r\n" +
                    "{0}\r\n" +
                    "{1}\r\n" +
                    "{2}\r\n",
                  var1, var2, var3);
    }
}

Not beautiful but it works.

Bronx
  • 747
  • 8
  • 12
mcmonkey4eva
  • 1,359
  • 7
  • 19
  • 1
    I assume OP wants to keep the verbatim string and avoid the `\r\n" +` at the end. – Tim Schmelter Jun 25 '13 at 21:30
  • Probably... but OP has to make a choice. There's no magic way to fit in indents and not use `\r\n" +`. He can do it this way, or the way in the OP - or actually he could do it how @Zdravko did it, which is probably a lot better... but that's a whole different thing. – mcmonkey4eva Jun 25 '13 at 21:31
  • +1 for stating the obvious. The language provides just two ways of constructing string literals -- a here-doc/at-string (`@"..."`), which are allowed to span lines in the source file or traditional string literals (`"..."`), which may not span lines in the source file. The compiler and optimizer will take care of constant-folding, so runtime performance isnt' an issue. – Nicholas Carey Jun 25 '13 at 21:35
  • Yeah this is how the code used to look before I changed it to a string literal. The literal is much cleaner, but just bothers me about the indentation in the middle of all my logic. Making it a resource makes the most sense for me. – Kevin DiTraglia Jun 25 '13 at 21:39
3

Define your string constant in another file and use the identifier in your code. It is a good practice to have all your strings in one place anyway if you need them translated, reviewed for compliance, etc..

Z .
  • 12,657
  • 1
  • 31
  • 56
  • Actually that's true I hadn't thought about it, but I could have it contain {0}...etc as a resource. Is that considered bad practice? – Kevin DiTraglia Jun 25 '13 at 21:34
  • I don't see why it would be a bad practice. My resources have {0} {1} in them... It's still better than having strings inside all your code... – Z . Jun 25 '13 at 21:36
3

When combining @ verbatim strings and the newer $ string interpolation you could apply the following trick: within { and } it is an expression (hence code) and not string data. Put your newline and your indent there and it will not show up in the formatted string. If your text does not contain expressions you can replace a space by an ' ' expression where you want a line break.

using System;

namespace ConsoleApplication6
{
    class Program
    {
        static void Main()
        {
            int count = 123;
            Console.WriteLine($@"Abc {count} def {count
                } ghi {
                count} jkl{' '
                }mno");
            Console.ReadLine();
        }
    }
}

Output:

Abc 123 def 123 ghi 123 jkl mno

  • 1
    Interesting idea, sadly it reads a little ugly and is confusing for someone who hasn't seen it. I'm sure there is also a cost to appending string.empty. Too bad there is no ignore returns / multiple space flags on verbatim strings. – vipero07 Aug 07 '18 at 14:27
3

Another "solution" is to wrap each such string in a #region and fold it. This does not really solve the problem, but at least it makes the code less of an eye-sore.

krylon
  • 49
  • 3
  • Good idea. Unfortunately only helps if you never look at the nitty gitty details of the file, though it might be still better than nothing... – Filip Skakun Jan 26 '23 at 00:16
1

The grammar for the C# language allows two ways of specifing string literals:

  • A here-doc/at-string (@"..."), which may span source file lines, or
  • An ordindary string literal ("..."), which may not.

Other means of getting the effect you want:

  1. Put your strings into resource files. This makes localization easier down the line.

  2. Add them to your assembly as embedded (file) resources:

An alternative, in the vein of other answers trying make it look nicer:

static readonly string MultilineLiteral = string.Join( Environment.NewLine , new string[]{
                                          @"line 1" ,
                                           "line 2" ,
                                          @"line 3" ,
                                          . . .
                                          "line n" ,
                                          }) ;
Community
  • 1
  • 1
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
0

Based on @Yegor answer (link) and if you want to take into account the line breaks within your text.

var code = @"
            public static loremIpsum(): lorem {


                return {

                    lorem: function() {
                        return 'foo'
                    },
                    ipsum: function() {
                        return 'bar'
                    },
                    dolor: function() {
                        return 'buzz'
                    }

                }
            }
".Dedent().TrimNewLines();

Console.WriteLine(code);

public static class StringExtensions
{
    public static string Dedent(this string @this)
    {
        var lines = @this.Split(Environment.NewLine)
            .Select(x => x.TrimEnd()).ToList();
        var trimLen = lines
            .FindAll(x => x != string.Empty)
            .Min(x => x.Length - x.TrimStart().Length);
        return string.Join(Environment.NewLine,
            lines.Select(x => x[Math.Min(x.Length, trimLen)..]));
    }

    public static string TrimNewLines(this string @this) => @this.Trim(Environment.NewLine.ToCharArray());
}

Result :

public static loremIpsum(): lorem {


    return {

        lorem: function() {
            return 'foo'
        },
        ipsum: function() {
            return 'bar'
        },
        dolor: function() {
            return 'buzz'
        }

    }
}
tr4cks
  • 126
  • 1
  • 8
-1

I, too have come across this problem.

Some times I put the entire thing on one line with \n (or \r\n for dos/windows).

Some times, I just ignore the ugly code formatting.

Other times, I put the text elsewhere - I think in c# you have #define, or even in a separate file or database, depending on the string in question. You still have to replace the variable - in c# I think you use string.Format?

I hope this is enough to get you started on a solution.

AMADANON Inc.
  • 5,753
  • 21
  • 31