• Hans (unregistered)

    Honestly, I want one of those jobs where you get paid for each line of code as well... :-(

  • ParkinT (cs)

    "//The way everyone does it."

    Yeah, there's a reason EVERYONE DOES IT this way.

    I love it!

  • ObiWayneKenobi (cs) in reply to ParkinT
    ParkinT:
    "//The way everyone does it."

    Yeah, there's a reason EVERYONE DOES IT this way.

    I love it!

    And it's called "being lazy". Same reason why people who use VB.NET (no, that's not the real WTF) use the VB6-lookalike shortcut functions instead of the language-agnostic .NET Runtime version (Left() and Right() instead of Substring, for example, or Len(Foo) instead of Foo.Length).

  • Drum D. (cs)

    "//The way everyone does it."

    1. Clean
    2. Easy to write
    3. Easy to read
    4. Easy to teach
    5. Easy to learn
    6. Easy to remember
    7. Elegant
    8. Available in most of the up-to-date programming languages ... (please feel free to continue the list)
  • joe.edwards (cs)

    On Windows platforms, "\r\n" == Environment.NewLine. If you know the only platform your software will run on is Windows (e.g. server-side code), then there's no real distinction.

  • snoofle (cs) in reply to ObiWayneKenobi

    But... but... but what if someday someone comes up with a new way of implying "cr-lf"? You absolutely MUST write encapsulated code in case this happens!

    Someone once actually told me not to use literal field specifiers (eg: "%d") in calls to printf in case some day they change the syntax. Right. It's only been unchanged since 1970, and a change like that would only break, oh, almost every program ever written.

  • A Nonny Mouse (cs)

    when a vb developer meets .net, coming through the rye..

  • Carra (unregistered)

    The real WTF is not using a StringBuilder!

  • sobani (cs)

    Windows has: "\r\n"

    Linux has: "\n"

    MacOS has: "\r"

    That's why you should use the Right Way instead of the Easy Way.

  • AbbydonKrafts (cs)

    The way "everyone does it" is technically wrong. As Joe Edwards pointed out, "\r\n" is the Windows new line. There is a reason for "Environment.NewLine". It allows porting between the frameworks (such as Mono). It will output the new line based on the host OS.

    Since the majority of .NET developers are probably developing for Windows, "\r\n" should be used; otherwise, stick with "the right way" of Environment.NewLine.

  • mfah (cs)

    At the very least I want to see a NewLineProvider. Plus it's not culture-neutral.

  • John (unregistered)

    No

    myString = string.Format("Line1{0}Line2{0}Line3", Environment.NewLine);

    is the correct way to do it.

  • dkf (unregistered) in reply to sobani
    sobani:
    MacOS has: "\r"
    In OS9 that was true, but OSX uses a Unix-style LF as a line separator.
  • Eric Shinn (unregistered)

    Reminds me of a WTF I saw the other day; if you output a CSV file and open it in Excel, Microsoft expects \r\n between rows but just \n for a newline within a cell. Of course if you open the CSV in Notepad it wants \r\n everywhere. Bah.

  • Anonymous (unregistered)

    The real WTF is .NET. Seriously, how can using an eighteen-letter character combination instead of a two letter one be "the Right Way?".

  • Cosmin Marginean (unregistered)

    Considering what I've seen on thedailywtf, I am expecting something like this any day now: public static string CHAR_A { get { char chrCR = 'A'; string strCR = chrCR.ToString(); return strCR; } }

    public static string CHAR_B { get { char chrCR = 'B'; string strCR = chrCR.ToString(); return strCR; } } ... public static string CHAR_SPACE { get { char chrCR = ' '; string strCR = chrCR.ToString(); return strCR; } } ...

  • Grovesy (cs) in reply to joe.edwards
    joe.edwards@imaginuity.com:
    On Windows platforms, "\r\n" == Environment.NewLine. If you know the only platform your software will run on is Windows (e.g. server-side code), then there's no real distinction.

    Personally, I nearly always go down the \r\n route... just looks tidier (if any of my code is around in 47 years, and Microsoft decided to go with one char for new lines instead of the two.. I'm very very sorry for creating the '2050 new line feed bug...'

    Only because I'll be 70 and probably won't be working then to cash in on the insane contractor rates we saw with the millennium bug

  • You'll also need... (unregistered) in reply to Cosmin Marginean
    Cosmin Marginean:
    Considering what I've seen on thedailywtf, I am expecting something like this any day now: public static string CHAR_A { get { char chrCR = 'A'; string strCR = chrCR.ToString(); return strCR; } }

    public static string CHAR_B { get { char chrCR = 'B'; string strCR = chrCR.ToString(); return strCR; } } ... public static string CHAR_SPACE { get { char chrCR = ' '; string strCR = chrCR.ToString(); return strCR; } } ...

    What about non latin-based alphabets? You must define a character-supplier interface, and implement it for all character sets. Then you will need a factory to produce the right type of "supplier". Of course, it must all be configurable via xml in case they come up with a new character set, or in case we meet and start working with space aliens who don't happen to use any of the ones we use!

  • sobani (cs) in reply to dkf
    dkf:
    sobani:
    MacOS has: "\r"
    In OS9 that was true, but OSX uses a Unix-style LF as a line separator.
    OK, MacOS used to have: "\r" :-P

    Anyway, a few years back the "\r" bit me when I used javascript to parse some user input.

    Back then, when I was still young and clueless...

    sobani, Living proof the clueless part doesn't auto-update when years pass :-/

  • Mizchief (unregistered)

    I would do it this way:

    myString = string.Format("Line1{0}Line2{0}Line3", Environment.NewLine);

    StringBuilders use too much overhead and are unesscarry in most cases.

  • fanguad (unregistered)
    You'll also need...:
    What about non latin-based alphabets? You must define a character-supplier interface, and implement it for all character sets. Then you will need a factory to produce the right type of "supplier". Of course, it must all be configurable via xml in case they come up with a new character set, or in case we meet and start working with space aliens who don't happen to use any of the ones we use!

    Don't be ridiculous. All space aliens speak English and have American accents.

  • Grovesy (cs) in reply to John
    John:
    No

    myString = string.Format("Line1{0}Line2{0}Line3", Environment.NewLine);

    is the correct way to do it.

    As the compiler will resolve

    string s1 = "line1" + enviroment.newline + "line2";

    string s2 = "line1\r\nline2";

    to the same thing at compile time (I belive... Enviroment.NewLine is either a constant, or a read only static variable. It should work out that they are both the same string, intern it and point both variables at the one value.

    string s = String.Format("line1{0}line2", Enviroment.NewLine);

    You are basicaly under the covers saying

    StringBuilder sb = new StringBuilder(); sb.Append("line1"); sb.Append(Enviroment.NewLine); sb.Append("line2"); return sb.ToString();

    Unless, the compiler is really really clever, this is going to have to be worked out at runtime.

    I'm sure someone will correct me with the StringBuilder argument if I'm wrong.. it's been a while since I've read up on strings and .net

  • Mythokia (unregistered)

    Wow! I didn't even know Environment.NewLine existed until I read this article. I've always been using \n.

  • Jason (unregistered)

    I've actually seen some .NET books teach using /n as the way to generate a new line instead of the "correct way."

  • Grovesy (cs) in reply to Mizchief
    Mizchief:
    I would do it this way:

    myString = string.Format("Line1{0}Line2{0}Line3", Environment.NewLine);

    StringBuilders use too much overhead and are unesscarry in most cases.

    String.Format uses StringBuilder.

    Which is why you should never ever do

    StringBuilder sb = new ...

    ... sb.Append(String.Format("hello {0}", someString);

    A second string builder is created.

  • Anon (unregistered) in reply to Jason

    I hope you mean "\n" not "/n" !?

  • Greg Beech (unregistered) in reply to Grovesy

    Constants and read-only statics are different.

    Constants are replaced at compile time, because they are constant and can be.

    The whole point of read-only statics is that they are NOT replaced at compile-time because they may not be constant (as Environment.NewLine isn't).

    So your concatenation examples are not the same.

  • DOA (cs)

    Reminds me of when I first started working and I had to connect to a remote app. For reasons that I barely remember I chose to simply open a socket and poured properly formatted text in it. I was using a linux system and the remote app was running on a windows machine. At the time I had no idea that different OSes used different characters for line returns and spent a day or two wondering why the hell the remote system wouldn't do what it was supposed to do. There was a lot of cursing when I finally figured it out...

  • Grovesy (cs) in reply to Greg Beech
    Greg Beech:
    Constants and read-only statics are different.

    Constants are replaced at compile time, because they are constant and can be.

    The whole point of read-only statics is that they are NOT replaced at compile-time because they may not be constant (as Environment.NewLine isn't).

    So your concatenation examples are not the same.

    Just realised that shortly have hitting post.. :)

    Anyhow, checking .net documentation... It is a Constant according to MSDN.

  • Aaron (cs) in reply to Anonymous
    Anonymous:
    The real WTF is .NET. Seriously, how can using an eighteen-letter character combination instead of a two letter one be "the Right Way?".
    For the same reason that:
    int x = 28;
    is the wrong way, and...
    private const int MinDaysInMonth = 28;
    // (later on)
    int daysInMonth = MinDaysInMonth;
    

    ...is the right way.

    In other words, the right way is to not be a lazy waste of skin, and instead take the extra 5 seconds to type out an extra couple of characters/words that will save the poor sap tasked with maintaining your piss-poor code a good half-hour or more.

    Get it?

  • Alan (unregistered) in reply to Grovesy
    Grovesy:
    As the compiler will resolve
    string s1 = "line1" + enviroment.newline + "line2";

    string s2 = "line1\r\nline2";

    to the same thing at compile time (I belive... Enviroment.NewLine is either a constant, or a read only static variable. It should work out that they are both the same string, intern it and point both variables at the one value.

    Bear in mind that compiletime == runtime for in .NET speak. Your code is compiled *as required*. Therefore you won't condense enviroment.newline to the correct character (or char sequence) until runtime, so on all platforms you'll get the correct sequence.
  • XIU (cs) in reply to Grovesy
    Grovesy:
    Just realised that shortly have hitting post.. :)

    Anyhow, checking .net documentation... It is a Constant according to MSDN.

    It's not a constant, it's even a property.

  • Kiss me I'm Polish (cs)

    DEFINE \n Environment.NewLine

  • aliquam (unregistered)

    Better do it the right way+Environment.NewLine++Environment.NewLine+Else we might end up on here.+Environment.NewLine+I'm just glad he didn't do something like+Environment.NewLine+

    public static string CRLF { get { return Environment.NewLine; } }
    +Environment.NewLine++Environment.NewLine+That might have been as funny as him casting a char to convert into a string on multiple lines...
  • Buddy (unregistered)

    Actually, I've done similar - maybe not so verbose.

    If you don't know which platform your code will run on, but you absolutely need to be certain that you write a CR/LF pair (or any kind of line termination) because you know what platform will read the file, specifying each character explicitly is the only way.

    In C, I'd use something like this:

    #define CRLF "\x0d\x0a"

    ...

    fprintf(file, "Ghastly weather%s" CRLF, (emphasize)? "!": ".");

  • Wyatt (unregistered) in reply to Carra
    Carra:
    The real WTF is not using a StringBuilder!

    For all the StringBuilder fans, myString = "Line1" + Environment.NewLine + "Line2" + Environment.NewLine + "Line3";

    gets emitted as a single call to string.Concat() with all the different parts as parameters. This largely negates any benefit you might see from StringBuilder.

  • GalacticCowboy (cs) in reply to A Nonny Mouse
    A Nonny Mouse:
    when a vb developer meets .net, coming through the rye..

    No, it's C#... VB has vbCrLf.

  • Rick (cs) in reply to sobani
    sobani:
    Windows has: "\r\n"

    Linux has: "\n"

    MacOS has: "\r"

    That's why you should use the Right Way instead of the Easy Way.

    There is extra credit for giving VMS end of line code. (hint, none of the above.)

  • alunharford (cs) in reply to Wyatt

    It completely blows away any benefits you might see from a StringBuilder. It's >10x faster, and much clearer.

  • Martin (unregistered) in reply to Anonymous
    Anonymous:
    The real WTF is .NET. Seriously, how can using an eighteen-letter character combination instead of a two letter one be "the Right Way?".

    It looks like that is the way .net is going. Anyone seen this :http://www.charlespetzold.com/etc/CSAML.html

  • Aaron (cs) in reply to Grovesy
    Grovesy:
    String.Format uses StringBuilder.

    Which is why you should never ever do

    StringBuilder sb = new ...

    ... sb.Append(String.Format("hello {0}", someString);

    A second string builder is created.

    Why should you "never ever" do that? Is there a shortage of StringBuilders that nobody has told me about?

    What if you're creating an error report that's 500 lines long and 20 of those lines have a few tokens to replace, like system info and such? Should you replace the entire thing with one string.Format(...) call with 50 parameters? Or are you instead suggesting that we replace the original 20 Format()ed lines with 80 repetitive Append calls?

    The purpose of a StringBuilder is to avoid the exponential performance associated with a pantload of immutable concatenations. So something like:

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 5000;="" i++)="" {="" sb.append("hello,="" "="" +="" "there.")="" }="">

    is still far, far better than the old

    string s = string.Empty;
    for (int i = 0; i < 5000;="" i++)="" {="" s="s" +="" "hello,="" "="" +="" "there.";="" }="">

    Yes, both ways are stupid in this contrived example, and if you are writing a really tight loop (which most people reading this have probably never had to write), then you might want to start worrying about things like this. But for the vast majority of programmers and programs, the important concept is just to use the mutable StringBuilder rather than creating the same immutable string over and over again through concats. It doesn't really matter if you use a regular old concat or Format() to feed one individual line to the StringBuilder.

    There's a balance to maintain between performance and code readability. Maybe you really need to optimize the hell out of some chunk of string-twiddling code, but maybe you don't, so never say never.

  • alien (unregistered) in reply to fanguad

    i dont

  • Grovesy (cs) in reply to XIU

    [quote user="XIU"][quote user="Grovesy"]Just realised that shortly have hitting post.. :)

    Anyhow, checking .net documentation... It is a Constant according to MSDN.[/quote]

    Thought I had better check this up, using C# with VS2005 against the .net 2 framework, taking the following code.. we get the following outputs after compilation

        public static void f1()
        {
            string x = "hello\r\nworld";
        }
    
        public static void f2()
        {
            string x = "hello" + Environment.NewLine + "world";
        }
    
        public static void f3()
        {
            string x = "hello" + "\r\n" + "world";
        }
    
        public static void f4()
        {
            string.Format("hello{0}world", "\r\n");
        }
        public static void f5()
        {
            string.Format("hello{0}world", Environment.NewLine);
        }
        public static void f6()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("hello");
            sb.Append(Environment.NewLine);
            sb.Append("world");
        }
    

    We have the following IL

    .method public hidebysig static void f1() cil managed { // Code size 8 (0x8) .maxstack 1 .locals init ([0] string x) IL_0000: nop IL_0001: ldstr "hello\r\nworld" IL_0006: stloc.0 IL_0007: ret } // end of method Program::f1

    .method public hidebysig static void f2() cil managed { // Code size 23 (0x17) .maxstack 3 .locals init ([0] string x) IL_0000: nop IL_0001: ldstr "hello" IL_0006: call string [mscorlib]System.Environment::get_NewLine() IL_000b: ldstr "world" IL_0010: call string [mscorlib]System.String::Concat(string, string, string) IL_0015: stloc.0 IL_0016: ret } // end of method Program::f2

    .method public hidebysig static void f3() cil managed { // Code size 8 (0x8) .maxstack 1 .locals init ([0] string x) IL_0000: nop IL_0001: ldstr "hello\r\nworld" IL_0006: stloc.0 IL_0007: ret } // end of method Program::f3

    .method public hidebysig static void f4() cil managed { // Code size 18 (0x12) .maxstack 8 IL_0000: nop IL_0001: ldstr "hello{0}world" IL_0006: ldstr "\r\n" IL_000b: call string [mscorlib]System.String::Format(string, object) IL_0010: pop IL_0011: ret } // end of method Program::f4

    .method public hidebysig static void f5() cil managed { // Code size 18 (0x12) .maxstack 8 IL_0000: nop IL_0001: ldstr "hello{0}world" IL_0006: call string [mscorlib]System.Environment::get_NewLine() IL_000b: call string [mscorlib]System.String::Format(string, object) IL_0010: pop IL_0011: ret } // end of method Program::f5

    .method public hidebysig static void f6() cil managed { // Code size 44 (0x2c) .maxstack 2 .locals init ([0] class [mscorlib]System.Text.StringBuilder sb) IL_0000: nop IL_0001: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: ldstr "hello" IL_000d: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) IL_0012: pop IL_0013: ldloc.0 IL_0014: call string [mscorlib]System.Environment::get_NewLine() IL_0019: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) IL_001e: pop IL_001f: ldloc.0 IL_0020: ldstr "world" IL_0025: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::Append(string) IL_002a: pop IL_002b: ret } // end of method Program::f6

  • [ICR] (unregistered)

    "string s = String.Format("line1{0}line2", Enviroment.NewLine);

    You are basicaly under the covers saying

    StringBuilder sb = new StringBuilder(); sb.Append("line1"); sb.Append(Enviroment.NewLine); sb.Append("line2"); return sb.ToString(); "

    Actually, it's saying

    StringBuilder sb = new StringBuilder(21); sb.AppendFormat(null, format, new object[] {Environment.NewLine}); return sb.ToString();

    But that's just being pedantic.

    And Environment.NewLine isn't a constant, it's a property, and thus can't be inlined.

    If you are just adding strings then "String1" + Environment.NewLine + "String2" is the best way. If you are doing it in a loop, say: for(int i = 0; i < 10; i++) { stringBuilder.Append("String" + i); } Then a StringBuilder is generally best. If you are doing something which concatenates a lot of strings in a potentially confusing way, then string.Format might be good: string printOut = string.Format(@"Name: {0} D/O/B: {1} Rank: {2}", name, dob.ToShortDateString(), rank); Here the syntax of the content of the string is much clearer, and we don't need to pollute it with things like our conversion of the date to a string.

  • WhiskeyJack (cs) in reply to Aaron
    Aaron:
    private const int MinDaysInMonth = 28;
    // (later on)
    int daysInMonth = MinDaysInMonth;
    
    ...is the right way.

    In other words, the right way is to not be a lazy waste of skin, and instead take the extra 5 seconds to type out an extra couple of characters/words that will save the poor sap tasked with maintaining your piss-poor code a good half-hour or more.

    Get it?

    Or, we could NOT fill our programs with global variables for no good reason. Do we expect the "min days in month" to one day change as the world moves to a new kind of calendar? Now when I debug this code I see that assignment and I have to do another code search to find MinDaysInMonth.

    int daysInMonth = 28;   // minimum number of days in a month
    

    If this value is used in multiple places, THEN of course it makes sense to declare it as a class variable or global.

  • Jim (unregistered) in reply to Anonymous
    Anonymous:
    The real WTF is .NET. Seriously, how can using an eighteen-letter character combination instead of a two letter one be "the Right Way?".

    It beats Java:

    System.getProperty("environment.newline");

    43 characters...And doing it in Java is more important as well, as it is cross platform, as opposed to .NET, which is Windows only. using "\n" is safe in .NET, as that is the windows newline for everything except notepad, which expects "\r\n".

    And I am aware of the Mono project to bring .NET out to everyone else, but that's not supported by Microsoft, who is really, the owners of .NET. Just because I can run World of Warcraft on Linux using Wine doesn't make it a Linux product, so running .NET on Linux/Mac using Mono doesn't make it a Linux product either.

  • SlyEcho (unregistered) in reply to Grovesy
    Grovesy:
    string s = String.Format("line1{0}line2", Enviroment.NewLine);
    

    You are basicaly under the covers saying

    StringBuilder sb = new StringBuilder();
    sb.Append("line1");
    sb.Append(Enviroment.NewLine);
    sb.Append("line2");
    return sb.ToString();
    

    More like:

    StringBuilder sb = new StringBuilder();
    sb.AppendFormat("line1{0}line2", Enviroment.NewLine);
    return sb.ToString();
    
    Grovesy:
    Unless, the compiler is really really clever, this is going to have to be worked out at runtime.

    Well, that would be a good thing if you wanted to run the same program on different platforms without recompiling.

  • Cosmin Marginean (unregistered) in reply to You'll also need...
    You'll also need...:
    Cosmin Marginean:
    Considering what I've seen on thedailywtf, I am expecting something like this any day now: public static string CHAR_A { get { char chrCR = 'A'; string strCR = chrCR.ToString(); return strCR; } }

    public static string CHAR_B { get { char chrCR = 'B'; string strCR = chrCR.ToString(); return strCR; } } ... public static string CHAR_SPACE { get { char chrCR = ' '; string strCR = chrCR.ToString(); return strCR; } } ...

    What about non latin-based alphabets? You must define a character-supplier interface, and implement it for all character sets. Then you will need a factory to produce the right type of "supplier". Of course, it must all be configurable via xml in case they come up with a new character set, or in case we meet and start working with space aliens who don't happen to use any of the ones we use!
    Damn you're right. I've been waiting a long time for something like: CharsetProviderFactory factory = CharsetProviderFactory.getXmlProviderFactory(); charset = factory.getCharsetProvider("/home/looser/pluto-dialect.xml"); String message = charset.I + charset.SPACE + charset.A + charset.M + charset.SPACE + charset.R + charset.E + charset.T + charset.A + charset.R + charset.D + charset.E + charset.D + charset.BACKSLASH + charset.ALIEN_SMILEY;

  • rick (unregistered)

    I've seen where newlines needed to be coded /r/n btw hence the Environment.Newline

  • Grovesy (cs) in reply to [ICR]
    [ICR]:
    "string s = String.Format("line1{0}line2", Enviroment.NewLine);

    You are basicaly under the covers saying

    StringBuilder sb = new StringBuilder(); sb.Append("line1"); sb.Append(Enviroment.NewLine); sb.Append("line2"); return sb.ToString(); "

    Actually, it's saying

    StringBuilder sb = new StringBuilder(21); sb.AppendFormat(null, format, new object[] {Environment.NewLine}); return sb.ToString();

    But that's just being pedantic.

    And Environment.NewLine isn't a constant, it's a property, and thus can't be inlined.

    If you are just adding strings then "String1" + Environment.NewLine + "String2" is the best way. If you are doing it in a loop, say: for(int i = 0; i < 10; i++) { stringBuilder.Append("String" + i); } Then a StringBuilder is generally best. If you are doing something which concatenates a lot of strings in a potentially confusing way, then string.Format might be good: string printOut = string.Format(@"Name: {0} D/O/B: {1} Rank: {2}", name, dob.ToShortDateString(), rank); Here the syntax of the content of the string is much clearer, and we don't need to pollute it with things like our conversion of the date to a string.

    Just to be clear, I have nothing against string.format or stringbuilder .. when used in correctly (I’ve met too many developers who String Build everything together). Or use string.format on string literals. StringBuilders that in turn call string.format will cause unnecessary garbage. Not a problem, only in extremely high load (we have an emailing system that knocks out some 100+ emails a second at peak times.. order confirmations, cancelations, delivery notifications, etc, we’d rather as little garbage to be cleaned up as possible)

    In terms of the NewLine being a constant, according to MSDN it is a constant in each version of the Framework (which I can't help thinking is a WTF), but wrapped up in a static property. I thought (incorrectly) that the compiler figured this out (the above IL output shows me to be wrong :)

Leave a comment on “His Own Way to Newline”

Log In or post as a guest

Replying to comment #:

« Return to Article