6
\$\begingroup\$

As a personal habit I generally prefer to use string replace and format methods, along with string "patterns", in lieu of string concatenation and other methods in most languages I use (typically C# and Javascript).

For example, if I want to generate a URL with parameters (C#), I use:

string url = "page.aspx?id={id}&ref={ref}";
return url.Replace("{id}", id)
          .Replace("{referrer}", referrer);

// could also be expressed as
return "page.aspx?id={id}&ref={ref}".Replace("{id}", id)
                                    .Replace("{referrer}", referrer);

Instead of:

return "page.aspx?id=" + id + "&ref=" & referrer;

Or:

// StringBuilder is typically more efficient than string concatenation
StringBuiler sbUrl = new StringBuilder();
sbUrl.Append("page.aspx?");
sbUrl.AppendFormat("id={0}", id);
sbUrl.AppendFormat("ref={0}", referrer);
return sbUrl.ToString();

I do the same in Javascript:

function generateUrl(p1, p2) {
    return "page.aspx?1={0}&2={1}".replace("{0}", p1)
                                  .replace("{1}", p2);
}

The reason is I prefer the readability of the code, especially when large amounts of textual data must be processed, such as in the case of a form letter or e-mail:

// this would typically be stored in an XML or other configuration file as a "pattern"
string body = @"Dear {name}:<br />
                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec aliquam blandit risus, quis imperdiet nibh congue quis.</p>
                <p>Pellentesque ullamcorper malesuada ante, ac auctor felis feugiat id. Etiam id eros convallis nisi feugiat tincidunt. Phasellus fringilla erat eu tortor egestas volutpat. Vestibulum in odio lorem, quis fringilla sapien.</p>
                <p>Sincerely,</p>
                <p class='sig'>{from-name}</p>";

return body.Replace("{name}", name).Replace("{from-name}", fName);

My concern is, however, that my methods are generally inefficient and that there may be better, more standardized ways of doing this.

Thoughts?

\$\endgroup\$
3
\$\begingroup\$
return "page.aspx?id={id}&ref={ref}".Replace("{id}", id)
                                    .Replace("{referrer}", referrer);

The above can be written since C# 6 and ECMAScript 6 as..

C#

return $"page.aspx?id={id}&ref={referrer}";

Javascript

return `page.aspx?id=${id}&ref=${referrer}`;
\$\endgroup\$
  • \$\begingroup\$ the $ within the C# string is not necessary. It should be return $"page.aspx?id={id}&ref={referrer}"; \$\endgroup\$ – Chad Levy Jul 19 at 22:13
  • \$\begingroup\$ @ChadLevy You are absolutely right, I've edited the string to be even more compact. \$\endgroup\$ – dfhwze Jul 20 at 5:31
  • 2
    \$\begingroup\$ You got the green tick after almost 8 years! I'll wait another 8 for the next groundbreaking C# improvement and steal it from you :-P \$\endgroup\$ – t3chb0t Jul 20 at 16:12
  • \$\begingroup\$ I would be honoured if you'd be the one to steal it from me ;-) \$\endgroup\$ – dfhwze Jul 20 at 16:22
  • 1
    \$\begingroup\$ I mulled over whether it made sense to mark it as the answer, but it seems that template literals were invented just for this reason. So now an 8 year old question can still be relevant (though it seems this activity made it get caught in the "on hold as off-topic" singularity). Maybe I'll fix the question in 8 years to make it on topic ;) \$\endgroup\$ – Chad Levy Jul 24 at 21:28
5
\$\begingroup\$

In C#, you could use the built-in string.Format instead:

string parameterizedUrl = "page.aspx?id={0}&ref={1}";
return string.Format(parameterizedUrl, id, referrer);

I haven't profiled it, but I think it would at worst be not any less efficient than your double Replace() call.

\$\endgroup\$
  • \$\begingroup\$ This is true, however String.Format does not support named parameters, only index-based ones. Named parameters typically help prevent off-by-one errors in my experience. \$\endgroup\$ – Chad Levy Sep 11 '11 at 21:46
  • 1
    \$\begingroup\$ @Paperjam That's a fair point, but I can honestly say I've never run into an off-by-one issue with string.Format that didn't quickly become apparent. I don't think it's any worse than having bugs due to typos in the named parameters (you even have to keep them synchronized between two places - the actual string and the arguments for Replace()). \$\endgroup\$ – Adam Lear Sep 11 '11 at 21:51
  • \$\begingroup\$ @Paperjam: If you asked me, I would much prefer to see the indexed parameters over named parameters in a format string. Since the BCL doesn't support names parameters, it's easier to identify as indexes than it is as names. The words too easily blends in with any surrounding text whereas indices are just numbers and stands out. Also their presence makes it obvious that it is a format string. \$\endgroup\$ – Jeff Mercado Sep 12 '11 at 6:28
  • \$\begingroup\$ I like Paperjam's idea, and I also would love for the compiler to check format statements. Because this simple-to-fix mistake is too common. \$\endgroup\$ – Daniel Williams Jan 17 '16 at 23:53
3
\$\begingroup\$

I'd tend towards format strings.

You may not of come across having to do localization, but some of the tools understand format strings which makes life easier for translators. It's much more conventional C#.

I do like the readability of named parameters though, and for localization, names would work a LOT better, but its not standard so its not supported.

\$\endgroup\$
3
\$\begingroup\$

Personally I would go with a String.Format() approach, but I think you should use whichever method is more readable. Trying to shave off a few microseconds here and there isn't worth the reduction in code readability and/or clarity for other people reading your code.

\$\endgroup\$
3
\$\begingroup\$

I'd use a format string instead. Any seasoned programmer will not find anything hard to read when using format strings. Though if you have multiple arguments for a multiple argument string (e.g., localization strings), it might be confusing with their orders or how much it requires, but they should be sufficiently documented anyway. It's a shame that the formatting features weren't a lot like python's. If you'd prefer not to use this approach for whatever reason, then I'm afraid you're catering to the wrong crowd for the wrong reasons IMHO.

Alternatively, I know you said that you'd prefer not to use this but using String.Concat() would most likely be the fastest and most efficient of these approaches. There isn't anything to parse nor are there any intermediate strings you need to work with. You get your complete string in one shot. Now I've gotta admit, it isn't the prettiest of syntaxes and I too avoid them at times but there are ways to make it a bit more attractive. But at least the arguments should be immediately identifiable as they'd typically not be string constants but variables (which are highlighted differently in any decent IDE).

First and foremost, your variable should always have the appropriate and descriptive names. This should be a given. Nothing kills readability as much as having crappy variable names, especially when you have a lot of them.

And when dealing with large bodies of text, nothing's stopping you from moving that text into a separate method to encapsulate it. That way you can give your parameters better names when needed, you'd find the string all in one place and it isn't mixed in with your code as much. These could easily be tied in with your resource files to make it that much better. This also applies to with using the format strings as well.

e.g.,

static string EmailText(string toName, string fromName)
{
    return @"Dear " + toName + @":<br />
             <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec aliquam blandit risus, quis imperdiet nibh congue quis.</p>
             <p>Pellentesque ullamcorper malesuada ante, ac auctor felis feugiat id. Etiam id eros convallis nisi feugiat tincidunt. Phasellus fringilla erat eu tortor egestas volutpat. Vestibulum in odio lorem, quis fringilla sapien.</p>
             <p>Sincerely,</p>
             <p class='sig'>" + fromName + @"</p>";
}

// then when you need it:
return EmailText(toName: "Bob", fromName: "Bill");  // named parameters optional but useful
\$\endgroup\$
  • \$\begingroup\$ Not in this case. The compiler will transform these "additions" to a single call to String.Concat() with each "addend" passed in as an argument. It will be exactly the same as if I called the method explicitly. It would be like you described if I had multiple uses of the += operator. That would be terrible. \$\endgroup\$ – Jeff Mercado Sep 12 '11 at 17:30
  • \$\begingroup\$ You're absolutely right. I should refrain from reading SE pre-coffee. :) I'll remove my original comment. Thanks! \$\endgroup\$ – Adam Lear Sep 12 '11 at 17:42
  • \$\begingroup\$ It's an important point though and is worth mentioning as it might be a motivation for people avoiding it for the wrong reasons. \$\endgroup\$ – Jeff Mercado Sep 12 '11 at 18:00
2
\$\begingroup\$

Efficiency-wise, you are already as fast as you can get without going to string concatenation or array joining as you are going to get without actually using them. (As you can see here: http://jsperf.com/string-test5)

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.