Related
I have a problem. I need to sum hours worked in an office in a code. The dates i get from SQL server thats no problem but i have different formats. For example: 2019. 09. 23. 14:54:23, 2019.09.23 14:54:23 or 2019-09-23 14:54:23; And i want to sum hours worked in result. No matter the year. Heres the example:
try
{
string betölt = "SELECT * from munkaorak where";
if (cbTech.Text != "")
{
betölt += " Munkaszam='" + cbMunka.Text + "' AND Részfolyamat='" + cbRész.Text + "' AND TechKod='" + cbTech.Text + "'";
}
else if (cbRész.Text != "")
{
betölt += " Munkaszam='" + cbMunka.Text + "' AND Részfolyamat='" + cbRész.Text + "'";
}
else if(cbMunka.Text !="")
{
betölt += " Munkaszam='" + cbMunka.Text + "'";
}
betölt += " order by ID DESC";
MySqlCommand name = new MySqlCommand(betölt, kapcsolat);
kapcsolat.Open();
olvasó = name.ExecuteReader();
int összora = 0;
if (olvasó.HasRows)
{
while (olvasó.Read())
{
if (olvasó.GetString(7) != "Befejezés: ")
{
string[] aha = olvasó.GetString(6).Split(' ');
string kezdes = aha[4];
string[] kezd = kezdes.Split(':');
int kezdoido = Convert.ToInt32(kezd[0]) * 60 * 60 + Convert.ToInt32(kezd[1]) * 60 + Convert.ToInt32(kezd[2]);
int befejezoido = 0;
string aha22 = "";
if (olvasó.GetString(7).IndexOf('-') >= 0)
{
string[] aha2 = olvasó.GetString(7).Split(' ');
string befejezes = aha2[1];
string[] bef = befejezes.Split(':');
aha22 = aha2[0].Split('-')[2];
befejezoido = Convert.ToInt32(bef[0]) * 60 * 60 + Convert.ToInt32(bef[1]) * 60 + Convert.ToInt32(bef[2]);
}
else
{
string[] aha2 = olvasó.GetString(7).Split(' ');
string befejezes = aha2[4];
string[] bef = befejezes.Split(':');
aha22 = aha2[3];
befejezoido = Convert.ToInt32(bef[0]) * 60 * 60 + Convert.ToInt32(bef[1]) * 60 + Convert.ToInt32(bef[2]);
}
string dolgozott = "";
if (aha[3].Replace(".", "") == aha22.Replace(".", ""))
{
dolgozott = mpbolora(befejezoido - kezdoido);
összora += befejezoido - kezdoido;
}
else
{
dolgozott = mpbolora((86400 - kezdoido) + befejezoido);
összora += (86400 - kezdoido) + befejezoido;
}
string validalo = "";
try
{
string[] validal = olvasó.GetString(9).Split(' ');
validalo = validal[0] + " " + validal[1] + " " + validal[2] + validal[3] + validal[4] + " " + validal[5];
}
catch
{
validalo = olvasó.GetString(9);
}
string munkafolyamat = olvasó.GetString(3) + "-" + olvasó.GetString(4) + "-" + olvasó.GetString(5);
string[] sorok = { olvasó.GetString(2), dolgozott, olvasó.GetString(6).Replace("Kezdés: ", ""), olvasó.GetString(7).Replace("Befejezés: ", ""), olvasó.GetString(8), validalo, munkafolyamat };
var lv = new ListViewItem(sorok);
lvStat.Items.Add(lv);
}
}
}
else
{
kapcsolat.Close();
MessageBox.Show("Nincs adat!", "Figyelem");
}
kapcsolat.Close();
lblÖssz.Text = "Összesen ledolgozott órák: " + mpbolora(összora);
}
catch (Exception a)
{
MessageBox.Show(a.Message);
kapcsolat.Close();
}
kapcsolat.Close();
It worked but when different formats appeared its not working because '-' or spaces. Please help!
In C#, there is a bunch of methods provided to convert strings that contain date times in many formats into a unified DateTime object. These methods can recognize quite a few standard date time formats, and if yours differ from them, you can even provide your own.
DateTime.Parse() - Converts a string to a DateTime object. If operation fails, it'll thrown an exception.
DateTime.TryParse() - Converts a string to a DateTime object only if possible. Returns true if successful, and false if it fails.
DateTime.TryParseExact() - Converts a string that is in the specified format into a DateTime object. Returns true if successful, and false otherwise.
In your case, you can use DateTime.TryParse() (which is recommended over simply using DateTime.Parse() unless you're absolutely sure the format is correct) like so:
var dtStr1 = " 2019. 09. 23. 14:54:23";
var dtStr2 = "2019.09.23 14:54:23";
var dtStr3 = "2019-09-23 14:54:23";
DateTime.TryParse(dtStr1, out DateTime dt1);
DateTime.TryParse(dtStr2, out DateTime dt2);
DateTime.TryParse(dtStr3, out DateTime dt3);
Once converted to a DateTime object, it no longer has a format associated with it. It's a structure, and hence only has member variables and methods. So to calculate total hours etc. you can use provided methods.
Say you want to calculate time between day's work start and end. You can convert those into DateTime objects, then subtract one from the others which will give you a TimeSpam object.
var dtStrStart = "2019.09.23 08:23:12";
var dtStrEnd = "2019.09.23 16:17:28";
DateTime.TryParse(dtStrStart, out DateTime dtStart);
DateTime.TryParse(dtStrEnd, out DateTime dtEnd);
var diff = dtEnd - dtStart;
Now the TimeSpan object, which is diff here, will give you a bunch of properties with difference in hours, minutes etc.
The TimeSpan.Days, TimeSpan.Minutes etc will give you the time in days, minutes etc.
Console.WriteLine(diff.Days);
Console.WriteLine(diff.Hours);
Console.WriteLine(diff.Minutes);
Console.WriteLine(diff.Seconds);
Console.WriteLine(diff.Milliseconds);
Output:
0
7
54
16
0
The TimeSpan.TotalMinutes etc will give you the entire time period in respective units.
Console.WriteLine(diff.TotalDays);
Console.WriteLine(diff.TotalHours);
Console.WriteLine(diff.TotalMinutes);
Console.WriteLine(diff.TotalSeconds);
Console.WriteLine(diff.TotalMilliseconds);
Output:
0.329351851851852
7.90444444444444
474.266666666667
28456
28456000
And conversely, when you're storing data in the database, you must again use a standard format, such as datetime or datetime2. It's advised you use datetime2, more info here.
Your code should look more like this:
try
{
MySqlCommand name = new MySqlCommand("SELECT * from munkaorak WHERE Munkaszam=#m", kapcsolat);
name.Parameters.AddWithValue("#m", cbMunka.Text);
if (cbRész.Text != "")
{
name.CommandText += " AND Részfolyamat=#r";
name.Parameters.AddWithValue("#r", cbRész.Text);
}
if (cbTech.Text != "")
{
name.CommandText += " AND TechKod=#t";
name.Parameters.AddWithValue("#t", cbTech.Text);
}
name.CommandText += " order by ID DESC"; //is it really necessary?
MySqlDataAdapter da = new MySqlDataAdapter(name);
DataTable dt = new DataTable();
da.Fill(dt);
foreach(DataRow ro in dt.Rows){
string fromStr = ro["YOUR_FROM_DATE_COLUMN_NAME"].ToString();
//cope with dates in varying formats
//by replacing all non-numeric chars with nothing
fromStr = Regex.Replace(fromStr, #"[^0-9]", "");
//now our dates of [2019. 09. 23. 14:54:23], [2019.09.23 14:54:23] or [2019-09-23 14:54:23]
//just become 20190923145423
DateTime fromDt = DateTime.ParseExact(fromStr, "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
string toStr = ro["YOUR_TO_DATE_COLUMN_NAME"].ToString();
toStr = Regex.Replace(toStr, #"[^0-9]", "");
DateTime toDt = DateTime.ParseExact(toStr, "yyyyMMddHHmmss", CultureInfo.InvariantCulture);
//total hours worked
(toDt - fromDt).TotalHours;
}
}
Hopefully that looks a lot simpler
Here you see no..:
Risky SQL injection hack possibility - don't concatenate values into your SQL, ever. Always concatenate a parameter in and then give a value to the parameter. Always
Difficult to read, lengthy string concatenation - looks terrible, always avoid it if you can
DB Connection opening and closing - micromanaging the database connection isn't necessary when using a dataadapter because it opens and closes for you
DataReader code full of magic numbers - GetString(7), hmmm.. was that the time in or time out? GetInt(4) - was it the age? The year? Here we get rid of all the datareader GetXX calls with their column ordinals and fill a DataTable (something like a 2D array) with rows that can be indexed by string names. It's still not as good as it can be (strongly typed DataTables are better) but it's a huge leap better than filling code with magic numbers, and working with everything in the most obscure, weakly typed way possible
Awkward time handling - it's gone in favour of Date parsing, because pulling strings to bits number by number, converting them to int, multiplying them by seconds and hours so they can be manipulated is tedious and hard to read - do away with it all by parsing these strings to the data types that they should have been stored as in the first place; you need to record the date and times that things happen at. Try your best to get that DB converted so these things are stored properly, and until then convert your strings to DateTime
Diffing dates using seconds: utilising TimeSpan calculations means no need to convert things to seconds, do crude math, drop all notions of time zones, or daylight savings changes etc; by using dates subtracted from each other you get a time period between those dates that takes things like daylight saving clock changes into account. Or even the ability to have one date that is tomorrow, or X days into the future. Might not matter for this app, but one day it could..
If you have MySQL 8 you can do the regex replace in the DB. Could even get the DB to diff and sum the dates.. We can't really make any recommendations on this point though because we don't know the column names
Let's say that you want to output or concat strings. Which of the following styles do you prefer?
var p = new { FirstName = "Bill", LastName = "Gates" };
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
Console.WriteLine(p.FirstName + " " + p.LastName);
Do you rather use format or do you simply concat strings? What is your favorite? Is one of these hurting your eyes?
Do you have any rational arguments to use one and not the other?
I'd go for the second one.
I'm amazed that so many people immediately want to find the code that executes the fastest. If ONE MILLION iterations STILL take less than a second to process, is this going to be in ANY WAY noticeable to the end user? Not very likely.
Premature optimization = FAIL.
I'd go with the String.Format option, only because it makes the most sense from an architectural standpoint. I don't care about the performance until it becomes an issue (and if it did, I'd ask myself: Do I need to concatenate a million names at once? Surely they won't all fit on the screen...)
Consider if your customer later wants to change it so that they can configure whether to display "Firstname Lastname" or "Lastname, Firstname." With the Format option, this is easy - just swap out the format string. With the concat, you'll need extra code. Sure that doesn't sound like a big deal in this particular example but extrapolate.
Try this code.
It's a slightly modified version of your code.
I removed Console.WriteLine as it's probably a few orders of magnitude slower than what I'm trying to measure.
I'm starting the Stopwatch before the loop and stopping it right after, this way I'm not losing precision if the function takes for example 26.4 ticks to execute.
The way you divided the result by some iterations was wrong. See what happens if you have 1,000 milliseconds and 100 milliseconds. In both situations, you will get 0 ms after dividing it by 1,000,000.
Code:
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
string result;
s.Start();
for (var i = 0; i < n; i++)
result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();
Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);
Those are my results:
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 618ms - 2213706 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 166ms - 595610 ticks
Oh dear - after reading one of the other replies I tried reversing the order of the operations - so performing the concatenation first, then the String.Format...
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks
So the order of the operations makes a HUGE difference, or rather the very first operation is ALWAYS much slower.
Here are the results of a run where operations are completed more than once. I have tried changing the orders but things generally follow the same rules, once the first result is ignored:
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks
As you can see subsequent runs of the same method (I refactored the code into 3 methods) are incrementally faster. The fastest appears to be the Console.WriteLine(String.Concat(...)) method, followed by normal concatenation, and then the formatted operations.
The initial delay in startup is likely the initialisation of Console Stream, as placing a Console.Writeline("Start!") before the first operation brings all times back into line.
Strings are immutable, this means the same tiny piece of memory is used over and over in your code. Adding the same two strings together and creating the same new string over and over again doesn't impact memory. .Net is smart enough just to use the same memory reference. Therefore your code doesn't truly test the difference between the two concat methods.
Try this on for size:
Stopwatch s = new Stopwatch();
int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0;
Random random = new Random(DateTime.Now.Millisecond);
string result;
s.Start();
for (var i = 0; i < n; i++)
result = (random.Next().ToString() + " " + random.Next().ToString());
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString());
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();
StringBuilder sb = new StringBuilder();
s.Start();
for(var i = 0; i < n; i++){
sb.Clear();
sb.Append(random.Next().ToString());
sb.Append(" ");
sb.Append(random.Next().ToString());
result = sb.ToString();
}
s.Stop();
sbElapsedMilliseconds = s.ElapsedMilliseconds;
sbElapsedTicks = s.ElapsedTicks;
s.Reset();
Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks");
Console.WriteLine("****************");
Console.WriteLine("Press Enter to Quit");
Console.ReadLine();
Sample Output:
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks
1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks
Pity the poor translators
If you know your application will stay in English, then fine, save the clock ticks. However, many cultures would usually see Lastname Firstname in, for instance, addresses.
So use string.Format(), especially if you're going to ever have your application go anywhere that English is not the first language.
Here are my results over 100,000 iterations:
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks
And here is the bench code:
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
for (var i = 0; i < n; i++)
{
s.Start();
Console.WriteLine(p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds += s.ElapsedMilliseconds;
cElapsedTicks += s.ElapsedTicks;
s.Reset();
s.Start();
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds += s.ElapsedMilliseconds;
fElapsedTicks += s.ElapsedTicks;
s.Reset();
}
Console.Clear();
Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");
So, I don't know whose reply to mark as an answer :)
Concatenating strings is fine in a simple scenario like that - it is more complicated with anything more complicated than that, even LastName, FirstName. With the format you can see, at a glance, what the final structure of the string will be when reading the code, with concatenation it becomes almost impossible to immediately discern the final result (except with a very simple example like this one).
What that means in the long run is that when you come back to make a change to your string format, you will either have the ability to pop in and make a few adjustments to the format string, or wrinkle your brow and start moving around all kinds of property accessors mixed with text, which is more likely to introduce problems.
If you're using .NET 3.5 you can use an extension method like this one and get an easy flowing, off the cuff syntax like this:
string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);
Finally, as your application grows in complexity you may decide that to sanely maintain strings in your application you want to move them into a resource file to localize or simply into a static helper. This will be MUCH easier to achieve if you have consistently used formats, and your code can be quite simply refactored to use something like
string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);
Starting from C# 6.0 interpolated strings can be used to do this, which simplifies the format even more.
var name = "Bill";
var surname = "Gates";
MessageBox.Show($"Welcome to the show, {name} {surname}!");
An interpolated string expression looks like a template string that contains expressions. An interpolated string expression creates a string by replacing the contained expressions with the ToString representations of the expressions’ results.
Interpolated strings have a similar performance to String.Format, but improved readability and shorter syntax, due to the fact that values and expressions are inserted in-line.
Please also refer to this dotnetperls article on string interpolation.
If you are looking for a default way to format your strings, this makes sense in terms of readability and performance (except if microseconds are going to make a difference in your specific use case).
For very simple manipulation I'd use concatenation, but once you get beyond 2 or 3 elements Format becomes more appropriate IMO.
Another reason to prefer String.Format is that .NET strings are immutable and doing it this way creates fewer temporary/intermediate copies.
While I totally understand the style preference and picked concatenation for my first answer partly based on my own preference, part of my decision was based on the thought that concatenation would be faster. So, out of curiosity, I tested it and the results were staggering, especially for such a small string.
Using the following code:
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
s.Start();
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
s.Stop();
Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");
s.Reset();
s.Start();
Console.WriteLine(p.FirstName + " " + p.LastName);
s.Stop();
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");
I got the following results:
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks
Using the formatting method is over 100 times slower!! Concatenation didn't even register as 1ms, which is why I output the timer ticks as well.
For basic string concatenation, I generally use the second style - easier to read and simpler. However, if I am doing a more complicated string combination I usually opt for String.Format.
String.Format saves on lots of quotes and pluses...
Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp);
vs
Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + ".");
Only a few charicters saved, but I think, in this example, format makes it much cleaner.
A week from now Aug 19, 2015, this question will be exactly seven (7) years old. There is now a better way of doing this. Better in terms of maintainability as I haven't done any performance test compared to just concatenating strings (but does it matter these days? a few milliseconds in difference?). The new way of doing it with C# 6.0:
var p = new { FirstName = "Bill", LastName = "Gates" };
var fullname = $"{p.FirstName} {p.LastName}";
This new feature is better, IMO, and actually better in our case as we have codes where we build querystrings whose values depends on some factors. Imagine one querystring where we have 6 arguments. So instead of doing a, for example:
var qs = string.Format("q1={0}&q2={1}&q3={2}&q4={3}&q5={4}&q6={5}",
someVar, anotherVarWithLongName, var3, var4, var5, var6)
in can be written like this and it's easier to read:
var qs=$"q1={someVar}&q2={anotherVarWithLongName}&q3={var3}&q4={var4}&q5={var5}&q6={var6}";
A better test would be to watch your memory using Perfmon and the CLR memory counters. My understanding is that the whole reason you want to use String.Format instead of just concatenating strings is since strings are immutable, you are unnecessarily burdening the garbage collector with temporary strings that need to be reclaimed in the next pass.
StringBuilder and String.Format, although potentially slower, is more memory efficient.
What is so bad about string concatenation?
Generally, I prefer the former, as especially when the strings get long it can be much easier to read.
The other benefit is I believe one of the performances, as the latter actually performs 2 string creation statements before passing the final string to the Console.Write method. String.Format uses a StringBuilder under the covers I believe, so multiple concatenations are avoided.
It should be noted however that if the parameters you are passing into String.Format (and other such methods like Console.Write) are value types then they will be boxed before passed in, which can provide its own performance hits. Blog post on this here.
Formatting is the “.NET” way of doing it. Certain refactoring tools (Refactor! for one) will even propose to refactor the concat-style code to use the formatting style.
Formatting is easier to optimize for the compiler (although the second will probably be refactored to use the 'Concat' method which is fast).
Formatting is usually clearer to read (especially with “fancy” formatting).
Formatting means implicit calls to '.ToString' on all variables, which is good for readability.
According to “Effective C#”, the .NET 'WriteLine' and 'Format' implementations are messed up, they autobox all value types (which is bad). “Effective C#” advises to perform '.ToString' calls explicitly, which IMHO is bogus (see Jeff's posting)
At the moment, formatting type hints are not checked by the compiler, resulting in runtime errors. However, this could be amended in future versions.
I choose based on readability.
I prefer the format option when there's some text around the variables. In this example:
Console.WriteLine("User {0} accessed {1} on {2}.",
user.Name, fileName, timestamp);
you understand the meaning even without variable names, whereas the concat is cluttered with quotes and + signs and confuses my eyes:
Console.WriteLine("User " + user.Name + " accessed " + fileName +
" on " + timestamp + ".");
(I borrowed Mike's example because I like it)
If the format string doesn't mean much without variable names, I have to use concat:
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
The format option makes me read the variable names and map them to the corresponding numbers. The concat option doesn't require that. I'm still confused by the quotes and + signs, but the alternative is worse. Ruby?
Console.WriteLine(p.FirstName + " " + p.LastName);
Performance-wise, I expect the format option to be slower than the concat, since the format requires the string to be parsed. I don't remember having to optimize this kind of instruction, but if I did, I'd look at string methods like Concat() and Join().
The other advantage of the format is that the format string can be put in a configuration file. Very handy with error messages and UI text.
I'd use the String.Format, but I would also have the format string in the resource files so it can be localised for other languages. Using a simple string concat doesn't allow you to do that. Obviously, if you won't ever need to localise that string, this isn't a reason to think about it. It really depends on what the string is for.
If it's going to be shown to the user, I'd use String.Format so I can localize if I need to - and FxCop will spell-check it for me, just in case :)
If it contains numbers or any other non-string things (e.g. dates), I'd use String.Format because it gives me more control over the formatting.
If it's for building a query like SQL, I'd use Linq.
If for concatenating strings inside a loop, I'd use StringBuilder to avoid performance problems.
If it's for some output the user won't see and isn't going to affect performance I'd use String.Format because I'm in the habit of using it anyway and I'm just used to it :)
If you're dealing with something that needs to be easy to read (and this is most code), I'd stick with the operator overload version UNLESS:
The code needs to be executed millions of times
You're doing tons of concats (more than 4 is a ton)
The code is targeted towards the Compact Framework
Under at least two of these circumstances, I would use StringBuilder instead.
If you intend to localise the result, then String.Format is essential because different natural languages might not even have the data in the same order.
I think this depends heavily on how complex the output is. I tend to choose whichever scenario works best at the time.
Pick the right tool based on the job :D Whichever looks cleanest!
I prefer the second as well but I have no rational arguments at this time to support that position.
Nice one!
Just added
s.Start();
for (var i = 0; i < n; i++)
result = string.Concat(p.FirstName, " ", p.LastName);
s.Stop();
ceElapsedMilliseconds = s.ElapsedMilliseconds;
ceElapsedTicks = s.ElapsedTicks;
s.Reset();
And it is even faster (I guess string.Concat is called in both examples, but the first one requires some sort of translation).
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks
1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks
Since I don't think the answers here cover everything, I'd like to make a small addition here.
Console.WriteLine(string format, params object[] pars) calls string.Format. The '+' implies string concatenation. I don't think this always has to do with style; I tend to mix the two styles depending on the context I'm in.
Short answer
The decision you're facing has to do with string allocation. I'll try to make it simple.
Say you have
string s = a + "foo" + b;
If you execute this, it will evaluate as follows:
string tmp1 = a;
string tmp2 = "foo"
string tmp3 = concat(tmp1, tmp2);
string tmp4 = b;
string s = concat(tmp3, tmp4);
tmp here is not really a local variable, but it is temporary for the JIT (it's pushed on the IL stack). If you push a string on the stack (such as ldstr in IL for literals), you put a reference to a string pointer on the stack.
The moment you call concat this reference becomes a problem because there isn't any string reference available that contains both strings. This means that .NET needs to allocate a new block of memory, and then fill it with the two strings. The reason this is a problem is that allocation is relatively expensive.
Which changes the question to: How can you reduce the number of concat operations?
So, the rough answer is: string.Format for >1 concats, '+' will work just fine for 1 concat. And if you don't care about doing micro-performance optimizations, string.Format will work just fine in the general case.
A note about Culture
And then there's something called culture...
string.Format enables you to use CultureInfo in your formatting. A simple operator '+' uses the current culture.
This is especially an important remark if you're writing file formats and f.ex. double values that you 'add' to a string. On different machines, you might end up with different strings if you don't use string.Format with an explicit CultureInfo.
F.ex. consider what happens if you change a '.' for a ',' while writing your comma-seperated-values file... in Dutch, the decimal separator is a comma, so your user might just get a 'funny' surprise.
More detailed answer
If you don't know the exact size of the string beforehand, it's best to use a policy like this to over allocate the buffers you use. The slack space is first filled, after which the data is copied in.
Growing means allocating a new block of memory and copying the old data to the new buffer. The old block of memory can then be released. You get the bottom line at this point: growing is an expensive operation.
The most practical way to do this is to use an overallocation policy. The most common policy is to over allocate buffers in powers of 2. Of course, you have to do it a bit smarter than that (since it makes no sense to grow from 1,2,4,8 if you already know you need 128 chars) but you get the picture. The policy ensures you don't need too many of the expensive operations I described above.
StringBuilder is a class that basically over allocates the underlying buffer in powers of two. string.Format uses StringBuilder under the hood.
This makes your decision a basic trade-off between over-allocate-and-append (-multiple) (w/w.o. culture) or just allocate-and-append.
I actually like the first one because when there are a lot of variables intermingled with the text it seems easier to read to me. Plus, it is easier to deal with quotes when using the string.Format(), uh, format. Here is decent analysis of string concatenation.
I've always gone the string.Format() route. Being able to store formats in variables like Nathan's example is a great advantage. In some cases I may append a variable but once more than 1 variable is being concatenated I refactor to use formatting.
Oh, and just for completeness, the following is a few ticks faster than normal concatenation:
Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));
The first one (format) looks better to me. It's more readable and you are not creating extra temporary string objects.
I was curious where StringBuilder stood with these tests. Results below...
class Program {
static void Main(string[] args) {
var p = new { FirstName = "Bill", LastName = "Gates" };
var tests = new[] {
new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) },
new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) },
new { Name = "StringBuilder", Action = new Action(delegate() {
StringBuilder sb = new StringBuilder();
sb.Append(p.FirstName);
sb.Append(" ");
sb.Append(p.LastName);
string x = sb.ToString();
}) }
};
var Watch = new Stopwatch();
foreach (var t in tests) {
for (int i = 0; i < 5; i++) {
Watch.Reset();
long Elapsed = ElapsedTicks(t.Action, Watch, 10000);
Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString()));
}
}
}
public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) {
Watch.Start();
for (int i = 0; i < Iterations; i++) {
ActionDelg();
}
Watch.Stop();
return Watch.ElapsedTicks / Iterations;
}
}
Results:
Concat: 406 ticks
Concat: 356 ticks
Concat: 411 ticks
Concat: 299 ticks
Concat: 266 ticks
Format: 5269 ticks
Format: 954 ticks
Format: 1004 ticks
Format: 984 ticks
Format: 974 ticks
StringBuilder: 629 ticks
StringBuilder: 484 ticks
StringBuilder: 482 ticks
StringBuilder: 508 ticks
StringBuilder: 504 ticks
According to the MCSD prep material, Microsoft suggests using the + operator when dealing with a very small number of concatenations (probably 2 to 4). I'm still not sure why, but it's something to consider.
The most readable would be to use the string interpolation feature of C# 6.0:
Console.WriteLine($"{p.FirstName} {p.LastName}");
Its performance is similar to using "+".
I'm trying to save a set of records in my desktop c# application and it appears to stop since one of the values was in the incorrect format.
Before saving, the system goes through these computations:
private void ComputeTotalWeight()
{
double TotalWeight;
TotalWeight = ((Convert.ToInt32(txtSmall.Text)) + (Convert.ToInt32(txtMedium.Text)) + (Convert.ToInt32(txtLarge.Text)) +
(Convert.ToInt32(txtExtralarge.Text))) * .285;
txtTotalweight.Text = String.Format("{0:#,##0}", TotalWeight);
}
private void ComputeTagsCollars()
{
int TagsCollars;
TagsCollars = Convert.ToInt32(txtSmall.Text) + Convert.ToInt32(txtMedium.Text)
+ Convert.ToInt32(txtLarge.Text) + Convert.ToInt32(txtExtralarge.Text);
txtTags.Text = String.Format("{0:#,##0}", TagsCollars);
txtCollars.Text = String.Format("{0:#,##0}", TagsCollars);
}
But once I save, it seems to be having a problem with the GrandTotal computation:
I suspect the error come from this computation:
private void ComputeGrandTotal()
{
double GrandTotal;
GrandTotal = (((Convert.ToInt32(txtSmall.Text) + Convert.ToInt32(txtMedium.Text) + Convert.ToInt32(txtLarge.Text) +
Convert.ToInt32(txtExtralarge.Text)) * .285) * 315);
double TagsCollars;
TagsCollars = Convert.ToInt32(txtSmall.Text) + Convert.ToInt32(txtMedium.Text) + Convert.ToInt32(txtLarge.Text) + Convert.ToInt32(txtExtralarge.Text);
txtTags.Text = String.Format("{0:#,##0}", TagsCollars);
txtCollars.Text = String.Format("{0:#,##0}", TagsCollars);
lblGrandtotal.Text = String.Format("{0:#,###,##0}", (GrandTotal + TagsCollars + TagsCollars));
}
I've tried commenting out all GrandTotal related values and functions, and the records begin to save with no problem. Here's a copy of my save function:
private void InsertNewRecord()
{
SqlCommand cmdInsert = new SqlCommand();
cmdInsert.Connection = cn;
cmdInsert.CommandType = CommandType.Text;
//cmdInsert.CommandType = CommandType.StoredProcedure;
cmdInsert.Parameters.AddWithValue("#QtySmall", Convert.ToInt32(txtSmall.Text));
cmdInsert.Parameters.AddWithValue("#QtyMedium", Convert.ToInt32(txtMedium.Text));
cmdInsert.Parameters.AddWithValue("#QtyLarge", Convert.ToInt32(txtLarge.Text));
cmdInsert.Parameters.AddWithValue("#QtyExtralarge", Convert.ToInt32(txtExtralarge.Text));
cmdInsert.Parameters.AddWithValue("#QtyTags", Convert.ToInt32(txtTags.Text));
cmdInsert.Parameters.AddWithValue("#QtyCollars", Convert.ToInt32(txtCollars.Text));
cmdInsert.Parameters.AddWithValue("#TotalWeight", Convert.ToInt32(txtTotalweight.Text));
cmdInsert.Parameters.AddWithValue("#NoWorkers", Convert.ToInt32(txtWorkersno.Text));
cmdInsert.Parameters.AddWithValue("#NoMachines", Convert.ToInt32(txtMachinesno.Text));
cmdInsert.Parameters.AddWithValue("#BomStatus", SqlDbType.VarChar).Value = txtStatus.SelectedItem.ToString();
cmdInsert.Parameters.AddWithValue("#StartDate", SqlDbType.DateTime).Value = dtpStart.Value;
cmdInsert.Parameters.AddWithValue("#EndDate", SqlDbType.DateTime).Value = dtpEnd.Value;
cmdInsert.Parameters.AddWithValue("#GrandTotal", Convert.ToInt32(lblGrandtotal.Text));
cmdInsert.CommandText = " INSERT INTO BillOfMaterials2 " + " (QtySmall, QtyMedium, QtyLarge, QtyExtralarge, QtyTags, QtyCollars, TotalWeight, NoWorkers, NoMachines, BomStatus, StartDate, EndDate, GrandTotal) VALUES (" + "#QtySmall, #QtyMedium, #QtyLarge, #QtyExtralarge, #QtyTags, #QtyCollars, #TotalWeight, #NoWorkers, #NoMachines, #BomStatus, #StartDate, #EndDate, #GrandTotal)";
//cmdInsert.CommandText = "spInsertBom";
cmdInsert.ExecuteNonQuery();
}
Any help would be much appreciated.
1st I would not attempt converting a textbox.text value to Int32 using convert; better to use
Int32 myint = 0;
Int32.TryParse(textbox.text, out myint) ;
This ensures that the text can be converted to an integer and if not you get 0 as a returned out value.
Then in your save method - your #GrandTotal parameter is trying to save to what datatype ? - what is the type in your database ?? do they match - if not you will get a format exception your data (Type) is not the same Format (type) as the column type.
The op does not have a valid number in the text box he has this:
lblGrandtotal.Text = String.Format("{0:#,###,##0}",
(GrandTotal + TagsCollars + TagsCollars));
This is why the code where he sets the parameter value = lblGrandtotal.Text it is not a number it has formatting commas etc.. he needs to remove those to make it work, using Int.TryParse would easily reveal this.
its starts here - first you are putting decimal in your format string with comma.
lblGrandtotal.Text = String.Format("**{0:#,###,##0}**", (GrandTotal + TagsCollars + TagsCollars));
also, later in your code you are storing INT int database, when its actually decimal.
and as mentioned by ken, use tryparse to convert the value from string to ....
There are several issues here some of the other posts touched on but, what really stands out (in my opinion) is you're not validating the input data, which is risky for many reasons, asking for future headaches and causing these issues. Also, there are standard numerical input controls you could use. If there's some reason you can't use them though, you should be validating the input and, if the data is not valid, handle it. Below is a quick way to validate and then handle invalid inputs.
private void ComputeGrandTotal()
{
//Since there are values that need to be validated and converted to integers for use in two calculations...
int smll, mdm, lg, xl;
//Validate the inputs can be converted and set the appropriate variable values at the same time
if (Int32.TryParse(txtSmall.Text, out smll) //using TryParse sets the integer variable values only if they can successfully be converted
&& Int32.TryParse(txtMedium.Text, out mdm)
&& Int32.TryParse(txtLarge.Text, out lg)
&& Int32.TryParse(txtExtralarge.Text, out xl)
)
{
int ttl = smll + mdm + lg + xl;
double GrandTotal, TagsCollars;
TagsCollars = ttl;
GrandTotal = TagsCollars * .285 * 315;
txtTags.Text = $"{TagsCollars:#,##0}"; //Resharper suggested simplification of String.Format("{0:#,##0}", TagsCollars)....I believe ReSharper
txtCollars.Text = $"{TagsCollars:#,##0}";
lblGrandtotal.Text = $"{(GrandTotal + TagsCollars + TagsCollars):#,###,##0}";
}
}
This will get the job done but it's pretty inflexible. Each input has to successfully convert to an integer or this will fail. A better, more time consuming approach would be something like this:
int smll;//, mdm, lg, xl;
try
{
smll = Convert.ToInt32(txtSmall.Text);
}
catch (FormatException)
{
smll = 0;
//txtSmall.Text value can't be converted to an integer
}
catch (Exception)
{
//some other issue occurred and you're probably better off just exiting entirely
return;
}
There are more flexible approaches out there, such as using number styles and such but, their flexibility comes at the price of you having to be more aware of the impact of what and how you're coding. Sometimes it's just safer to train your customers than write code you're not confident with.
You are trying to parse an integer in lblGrantotal.Text, but you are getting a FormatException, which means the text in lblGranTotal isn't recognized as a number. Maybe you are using comma , instead of point . as decimal separator, or something like that.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
C# String output: format or concat?
what is the benefit of using this:
Console.WriteLine("{0}: {1}", e.Code, e.Reason);
VS. this:
Console.WriteLine(e.Code + ": " + e.Reason);
????
The reason I always use .Format() is for readability and consequently bug reduction and maintenance benefits. Of course this makes no sense if you're relatively new to code as the second example is more intuitive at first. The first appears needlessly complex.
However if you choose the second pattern then you start to have problems when you have lots of variables. This is where it is easier to appreciate the benefits of the first pattern.
e.g
Console.Writeline(var1 + " " + var2 + "-" + var3 +
" some sort of additional text" + var4);
Note the bug: I need another space after "text" but that's not easy to see in this example.
However if I do it the other way:
Console.Writeline("{0} {1}-{2} some sort of additional text{3}",
var1, var2, var3, var4)
It's clearer to see what's going on. Its easier to appreciate the final result when you split the formatting from the variables that are going to be used.
If we want to think even further long term then it helps with globalisation/customisation. If we put those format strings into config we can then change the formatting or ordering of the variables without touching the code.
For me, the benefits of the string.Format pendant are:
Improved readability
Better translatable
From a performance perspective, I did never do any measurements; it could well be that the concatenation is faster then the string.Format pendant.
Practically, the only difference is that the first allows you to control the layout
string.Format("*{0:3}*",1); // * 1*
or control formatting:
string.Format("*{0:c}*",1); // *$1.00*
The performance of concatenation can be considered when doing lots of it in tight loops, in which case StringBuilder.Append and StringBuilder.AppendFormat are both much preferred. AppendFormat and string.Format are very close, performance-wise, so there is no need to substitute the second for the first.
It boils down to "When is it better to use String.Format vs string concatenation".
See this question for the answer:
When is it better to use String.Format vs string concatenation?
As strings are immutable (cant change an existing string must create a new one each time) string format can have performance benefits as it wont create as many memory references.
result = string1 + " " + string2 + " " + string3
This creates 10 references
result
string1
string2
string3
" "
" "
string1 + " "
string1 + " " + string2
string1 + " " + string2 + " "
string1 + " " + string2 + " " + string 3
result = string.Format("{0} {1} {2}", string1, string2, string3)
This creates 5 references
result
string1
string2
string3
"{0} {1} {2}"
Let's say that you want to output or concat strings. Which of the following styles do you prefer?
var p = new { FirstName = "Bill", LastName = "Gates" };
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
Console.WriteLine(p.FirstName + " " + p.LastName);
Do you rather use format or do you simply concat strings? What is your favorite? Is one of these hurting your eyes?
Do you have any rational arguments to use one and not the other?
I'd go for the second one.
I'm amazed that so many people immediately want to find the code that executes the fastest. If ONE MILLION iterations STILL take less than a second to process, is this going to be in ANY WAY noticeable to the end user? Not very likely.
Premature optimization = FAIL.
I'd go with the String.Format option, only because it makes the most sense from an architectural standpoint. I don't care about the performance until it becomes an issue (and if it did, I'd ask myself: Do I need to concatenate a million names at once? Surely they won't all fit on the screen...)
Consider if your customer later wants to change it so that they can configure whether to display "Firstname Lastname" or "Lastname, Firstname." With the Format option, this is easy - just swap out the format string. With the concat, you'll need extra code. Sure that doesn't sound like a big deal in this particular example but extrapolate.
Try this code.
It's a slightly modified version of your code.
I removed Console.WriteLine as it's probably a few orders of magnitude slower than what I'm trying to measure.
I'm starting the Stopwatch before the loop and stopping it right after, this way I'm not losing precision if the function takes for example 26.4 ticks to execute.
The way you divided the result by some iterations was wrong. See what happens if you have 1,000 milliseconds and 100 milliseconds. In both situations, you will get 0 ms after dividing it by 1,000,000.
Code:
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
string result;
s.Start();
for (var i = 0; i < n; i++)
result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();
Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);
Those are my results:
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 618ms - 2213706 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 166ms - 595610 ticks
Oh dear - after reading one of the other replies I tried reversing the order of the operations - so performing the concatenation first, then the String.Format...
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks
So the order of the operations makes a HUGE difference, or rather the very first operation is ALWAYS much slower.
Here are the results of a run where operations are completed more than once. I have tried changing the orders but things generally follow the same rules, once the first result is ignored:
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks
As you can see subsequent runs of the same method (I refactored the code into 3 methods) are incrementally faster. The fastest appears to be the Console.WriteLine(String.Concat(...)) method, followed by normal concatenation, and then the formatted operations.
The initial delay in startup is likely the initialisation of Console Stream, as placing a Console.Writeline("Start!") before the first operation brings all times back into line.
Strings are immutable, this means the same tiny piece of memory is used over and over in your code. Adding the same two strings together and creating the same new string over and over again doesn't impact memory. .Net is smart enough just to use the same memory reference. Therefore your code doesn't truly test the difference between the two concat methods.
Try this on for size:
Stopwatch s = new Stopwatch();
int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0;
Random random = new Random(DateTime.Now.Millisecond);
string result;
s.Start();
for (var i = 0; i < n; i++)
result = (random.Next().ToString() + " " + random.Next().ToString());
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString());
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();
StringBuilder sb = new StringBuilder();
s.Start();
for(var i = 0; i < n; i++){
sb.Clear();
sb.Append(random.Next().ToString());
sb.Append(" ");
sb.Append(random.Next().ToString());
result = sb.ToString();
}
s.Stop();
sbElapsedMilliseconds = s.ElapsedMilliseconds;
sbElapsedTicks = s.ElapsedTicks;
s.Reset();
Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks");
Console.WriteLine("****************");
Console.WriteLine("Press Enter to Quit");
Console.ReadLine();
Sample Output:
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks
1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks
Pity the poor translators
If you know your application will stay in English, then fine, save the clock ticks. However, many cultures would usually see Lastname Firstname in, for instance, addresses.
So use string.Format(), especially if you're going to ever have your application go anywhere that English is not the first language.
Here are my results over 100,000 iterations:
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks
And here is the bench code:
Stopwatch s = new Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;
for (var i = 0; i < n; i++)
{
s.Start();
Console.WriteLine(p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds += s.ElapsedMilliseconds;
cElapsedTicks += s.ElapsedTicks;
s.Reset();
s.Start();
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds += s.ElapsedMilliseconds;
fElapsedTicks += s.ElapsedTicks;
s.Reset();
}
Console.Clear();
Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");
So, I don't know whose reply to mark as an answer :)
Concatenating strings is fine in a simple scenario like that - it is more complicated with anything more complicated than that, even LastName, FirstName. With the format you can see, at a glance, what the final structure of the string will be when reading the code, with concatenation it becomes almost impossible to immediately discern the final result (except with a very simple example like this one).
What that means in the long run is that when you come back to make a change to your string format, you will either have the ability to pop in and make a few adjustments to the format string, or wrinkle your brow and start moving around all kinds of property accessors mixed with text, which is more likely to introduce problems.
If you're using .NET 3.5 you can use an extension method like this one and get an easy flowing, off the cuff syntax like this:
string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);
Finally, as your application grows in complexity you may decide that to sanely maintain strings in your application you want to move them into a resource file to localize or simply into a static helper. This will be MUCH easier to achieve if you have consistently used formats, and your code can be quite simply refactored to use something like
string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);
Starting from C# 6.0 interpolated strings can be used to do this, which simplifies the format even more.
var name = "Bill";
var surname = "Gates";
MessageBox.Show($"Welcome to the show, {name} {surname}!");
An interpolated string expression looks like a template string that contains expressions. An interpolated string expression creates a string by replacing the contained expressions with the ToString representations of the expressions’ results.
Interpolated strings have a similar performance to String.Format, but improved readability and shorter syntax, due to the fact that values and expressions are inserted in-line.
Please also refer to this dotnetperls article on string interpolation.
If you are looking for a default way to format your strings, this makes sense in terms of readability and performance (except if microseconds are going to make a difference in your specific use case).
For very simple manipulation I'd use concatenation, but once you get beyond 2 or 3 elements Format becomes more appropriate IMO.
Another reason to prefer String.Format is that .NET strings are immutable and doing it this way creates fewer temporary/intermediate copies.
While I totally understand the style preference and picked concatenation for my first answer partly based on my own preference, part of my decision was based on the thought that concatenation would be faster. So, out of curiosity, I tested it and the results were staggering, especially for such a small string.
Using the following code:
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
var p = new { FirstName = "Bill", LastName = "Gates" };
s.Start();
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
s.Stop();
Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");
s.Reset();
s.Start();
Console.WriteLine(p.FirstName + " " + p.LastName);
s.Stop();
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");
I got the following results:
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks
Using the formatting method is over 100 times slower!! Concatenation didn't even register as 1ms, which is why I output the timer ticks as well.
For basic string concatenation, I generally use the second style - easier to read and simpler. However, if I am doing a more complicated string combination I usually opt for String.Format.
String.Format saves on lots of quotes and pluses...
Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp);
vs
Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + ".");
Only a few charicters saved, but I think, in this example, format makes it much cleaner.
A week from now Aug 19, 2015, this question will be exactly seven (7) years old. There is now a better way of doing this. Better in terms of maintainability as I haven't done any performance test compared to just concatenating strings (but does it matter these days? a few milliseconds in difference?). The new way of doing it with C# 6.0:
var p = new { FirstName = "Bill", LastName = "Gates" };
var fullname = $"{p.FirstName} {p.LastName}";
This new feature is better, IMO, and actually better in our case as we have codes where we build querystrings whose values depends on some factors. Imagine one querystring where we have 6 arguments. So instead of doing a, for example:
var qs = string.Format("q1={0}&q2={1}&q3={2}&q4={3}&q5={4}&q6={5}",
someVar, anotherVarWithLongName, var3, var4, var5, var6)
in can be written like this and it's easier to read:
var qs=$"q1={someVar}&q2={anotherVarWithLongName}&q3={var3}&q4={var4}&q5={var5}&q6={var6}";
A better test would be to watch your memory using Perfmon and the CLR memory counters. My understanding is that the whole reason you want to use String.Format instead of just concatenating strings is since strings are immutable, you are unnecessarily burdening the garbage collector with temporary strings that need to be reclaimed in the next pass.
StringBuilder and String.Format, although potentially slower, is more memory efficient.
What is so bad about string concatenation?
Generally, I prefer the former, as especially when the strings get long it can be much easier to read.
The other benefit is I believe one of the performances, as the latter actually performs 2 string creation statements before passing the final string to the Console.Write method. String.Format uses a StringBuilder under the covers I believe, so multiple concatenations are avoided.
It should be noted however that if the parameters you are passing into String.Format (and other such methods like Console.Write) are value types then they will be boxed before passed in, which can provide its own performance hits. Blog post on this here.
Formatting is the “.NET” way of doing it. Certain refactoring tools (Refactor! for one) will even propose to refactor the concat-style code to use the formatting style.
Formatting is easier to optimize for the compiler (although the second will probably be refactored to use the 'Concat' method which is fast).
Formatting is usually clearer to read (especially with “fancy” formatting).
Formatting means implicit calls to '.ToString' on all variables, which is good for readability.
According to “Effective C#”, the .NET 'WriteLine' and 'Format' implementations are messed up, they autobox all value types (which is bad). “Effective C#” advises to perform '.ToString' calls explicitly, which IMHO is bogus (see Jeff's posting)
At the moment, formatting type hints are not checked by the compiler, resulting in runtime errors. However, this could be amended in future versions.
I choose based on readability.
I prefer the format option when there's some text around the variables. In this example:
Console.WriteLine("User {0} accessed {1} on {2}.",
user.Name, fileName, timestamp);
you understand the meaning even without variable names, whereas the concat is cluttered with quotes and + signs and confuses my eyes:
Console.WriteLine("User " + user.Name + " accessed " + fileName +
" on " + timestamp + ".");
(I borrowed Mike's example because I like it)
If the format string doesn't mean much without variable names, I have to use concat:
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
The format option makes me read the variable names and map them to the corresponding numbers. The concat option doesn't require that. I'm still confused by the quotes and + signs, but the alternative is worse. Ruby?
Console.WriteLine(p.FirstName + " " + p.LastName);
Performance-wise, I expect the format option to be slower than the concat, since the format requires the string to be parsed. I don't remember having to optimize this kind of instruction, but if I did, I'd look at string methods like Concat() and Join().
The other advantage of the format is that the format string can be put in a configuration file. Very handy with error messages and UI text.
I'd use the String.Format, but I would also have the format string in the resource files so it can be localised for other languages. Using a simple string concat doesn't allow you to do that. Obviously, if you won't ever need to localise that string, this isn't a reason to think about it. It really depends on what the string is for.
If it's going to be shown to the user, I'd use String.Format so I can localize if I need to - and FxCop will spell-check it for me, just in case :)
If it contains numbers or any other non-string things (e.g. dates), I'd use String.Format because it gives me more control over the formatting.
If it's for building a query like SQL, I'd use Linq.
If for concatenating strings inside a loop, I'd use StringBuilder to avoid performance problems.
If it's for some output the user won't see and isn't going to affect performance I'd use String.Format because I'm in the habit of using it anyway and I'm just used to it :)
If you're dealing with something that needs to be easy to read (and this is most code), I'd stick with the operator overload version UNLESS:
The code needs to be executed millions of times
You're doing tons of concats (more than 4 is a ton)
The code is targeted towards the Compact Framework
Under at least two of these circumstances, I would use StringBuilder instead.
If you intend to localise the result, then String.Format is essential because different natural languages might not even have the data in the same order.
I think this depends heavily on how complex the output is. I tend to choose whichever scenario works best at the time.
Pick the right tool based on the job :D Whichever looks cleanest!
I prefer the second as well but I have no rational arguments at this time to support that position.
Nice one!
Just added
s.Start();
for (var i = 0; i < n; i++)
result = string.Concat(p.FirstName, " ", p.LastName);
s.Stop();
ceElapsedMilliseconds = s.ElapsedMilliseconds;
ceElapsedTicks = s.ElapsedTicks;
s.Reset();
And it is even faster (I guess string.Concat is called in both examples, but the first one requires some sort of translation).
1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks
1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks
Since I don't think the answers here cover everything, I'd like to make a small addition here.
Console.WriteLine(string format, params object[] pars) calls string.Format. The '+' implies string concatenation. I don't think this always has to do with style; I tend to mix the two styles depending on the context I'm in.
Short answer
The decision you're facing has to do with string allocation. I'll try to make it simple.
Say you have
string s = a + "foo" + b;
If you execute this, it will evaluate as follows:
string tmp1 = a;
string tmp2 = "foo"
string tmp3 = concat(tmp1, tmp2);
string tmp4 = b;
string s = concat(tmp3, tmp4);
tmp here is not really a local variable, but it is temporary for the JIT (it's pushed on the IL stack). If you push a string on the stack (such as ldstr in IL for literals), you put a reference to a string pointer on the stack.
The moment you call concat this reference becomes a problem because there isn't any string reference available that contains both strings. This means that .NET needs to allocate a new block of memory, and then fill it with the two strings. The reason this is a problem is that allocation is relatively expensive.
Which changes the question to: How can you reduce the number of concat operations?
So, the rough answer is: string.Format for >1 concats, '+' will work just fine for 1 concat. And if you don't care about doing micro-performance optimizations, string.Format will work just fine in the general case.
A note about Culture
And then there's something called culture...
string.Format enables you to use CultureInfo in your formatting. A simple operator '+' uses the current culture.
This is especially an important remark if you're writing file formats and f.ex. double values that you 'add' to a string. On different machines, you might end up with different strings if you don't use string.Format with an explicit CultureInfo.
F.ex. consider what happens if you change a '.' for a ',' while writing your comma-seperated-values file... in Dutch, the decimal separator is a comma, so your user might just get a 'funny' surprise.
More detailed answer
If you don't know the exact size of the string beforehand, it's best to use a policy like this to over allocate the buffers you use. The slack space is first filled, after which the data is copied in.
Growing means allocating a new block of memory and copying the old data to the new buffer. The old block of memory can then be released. You get the bottom line at this point: growing is an expensive operation.
The most practical way to do this is to use an overallocation policy. The most common policy is to over allocate buffers in powers of 2. Of course, you have to do it a bit smarter than that (since it makes no sense to grow from 1,2,4,8 if you already know you need 128 chars) but you get the picture. The policy ensures you don't need too many of the expensive operations I described above.
StringBuilder is a class that basically over allocates the underlying buffer in powers of two. string.Format uses StringBuilder under the hood.
This makes your decision a basic trade-off between over-allocate-and-append (-multiple) (w/w.o. culture) or just allocate-and-append.
I actually like the first one because when there are a lot of variables intermingled with the text it seems easier to read to me. Plus, it is easier to deal with quotes when using the string.Format(), uh, format. Here is decent analysis of string concatenation.
I've always gone the string.Format() route. Being able to store formats in variables like Nathan's example is a great advantage. In some cases I may append a variable but once more than 1 variable is being concatenated I refactor to use formatting.
Oh, and just for completeness, the following is a few ticks faster than normal concatenation:
Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));
The first one (format) looks better to me. It's more readable and you are not creating extra temporary string objects.
I was curious where StringBuilder stood with these tests. Results below...
class Program {
static void Main(string[] args) {
var p = new { FirstName = "Bill", LastName = "Gates" };
var tests = new[] {
new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) },
new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) },
new { Name = "StringBuilder", Action = new Action(delegate() {
StringBuilder sb = new StringBuilder();
sb.Append(p.FirstName);
sb.Append(" ");
sb.Append(p.LastName);
string x = sb.ToString();
}) }
};
var Watch = new Stopwatch();
foreach (var t in tests) {
for (int i = 0; i < 5; i++) {
Watch.Reset();
long Elapsed = ElapsedTicks(t.Action, Watch, 10000);
Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString()));
}
}
}
public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) {
Watch.Start();
for (int i = 0; i < Iterations; i++) {
ActionDelg();
}
Watch.Stop();
return Watch.ElapsedTicks / Iterations;
}
}
Results:
Concat: 406 ticks
Concat: 356 ticks
Concat: 411 ticks
Concat: 299 ticks
Concat: 266 ticks
Format: 5269 ticks
Format: 954 ticks
Format: 1004 ticks
Format: 984 ticks
Format: 974 ticks
StringBuilder: 629 ticks
StringBuilder: 484 ticks
StringBuilder: 482 ticks
StringBuilder: 508 ticks
StringBuilder: 504 ticks
According to the MCSD prep material, Microsoft suggests using the + operator when dealing with a very small number of concatenations (probably 2 to 4). I'm still not sure why, but it's something to consider.
The most readable would be to use the string interpolation feature of C# 6.0:
Console.WriteLine($"{p.FirstName} {p.LastName}");
Its performance is similar to using "+".