Saturday, October 11, 2014

String Concatenation

One of the things beginner .NET developers should have learned is that it’s not a good idea to add strings (e.g. s = s + “text”)  in .NET languages to concatenate them. The reason for this is that .NET string are immutable, meaning they cannot be changed once created. Because of that, each time you add strings it’s creates a new string object which replaces the existing string, thus leaving an object that needs to be disposed of. For the occasional concatenation this isn’t a big problem, but if you are doing this in a loop you could end up creating a lot of unnecessary objects. The solution to this problem is to use the System.Text.StringBuilder object which let’s you concatenate strings without creating extra objects.
In this article I want to take a closer look at string concatenation to understand what is actually happening behind the scenes. Let’s start with this block of code

string s1 = "one";
string s2 = "two";
string s3 = s1 + s2;

If we compile this code and then de-compile it  (in this case I am using Telerik’s JustDecompile) here is the resulting code:

string s1 = "one";
string s2 = "two";
string s3 = string.Concat(s1, s2);

You will notice that the addition of strings was replaced by the compiler with a call to the string.Concat function. Let’s take a look at the code for Concat. I have stripped out some of the code from the function just to show the important part.
int length = str0.Length;
string str = string.FastAllocateString(length + str1.Length);
string.FillStringChecked(str, 0, str0);
string.FillStringChecked(str, length, str1);
return str;

This code uses FastAllocateString to create a new string object that is big enough to hold both of the strings being concatenated. It then uses FillStringChecked to place the two strings in the correct spots in the new string. Finally it returns the new string. Here is where you can see the new object being created which replaces the old object that was in s3 in our original code.

There are versions of the String.Concat function that take up to four strings. So if you were to do s5 = s1+s2+s3+s4, this would call String.Concat(s1,s2,s3,s4). What happens after four strings? For example this code:

string s1 = "one";
string s2 = "two";
string s3 = "three";
string s4 = "four";
string s5 = "five";
string s6 = s1 + s2 +s3 +s4 +s5;

compiles to:
string s1 = "one";
string s2 = "two";
string s3 = "three";
string s4 = "four";
string s5 = "five";
string[] strArrays = new string[5];
strArrays[0] = s1;
strArrays[1] = s2;
strArrays[2] = s3;
strArrays[3] = s4;
strArrays[4] = s5;
string s6 = string.Concat(strArrays);

This version of Concat will scan through the array adding up the total length of all the strings, create a new string this length and then load each string into the correct location. This does create one extra object, the array, but it doesn’t create a new object for each concatenation.

One other thing to note about string concatenations. If for some reason you did something like this, maybe for purposes of code clarity:

string s1 = "one" + "two" + "three" + "four";
Console.WriteLine(s1);

the resulting code will look like this:

string s1 = "onetwothreefour";
Console.WriteLine(s1);

The compiler is smart enough to know that you are adding together 4 string literals so it automatically creates a single literal for you.
In conclusion, concatenation isn’t always bad in .NET programs. As long as all the concatenation happens in one line you don’t end up with a lot of extra objects. But if you are concatenating to a single string over multiple lines or within a loop, it’s a good idea to use a StringBuilder instead.

Sunday, June 29, 2014

Excel Graphs with EPPlus

In my last post I showed how to write data to an Excel file using the EPPlus library. I this post we will look at a more advanced topic, how to create a graph on an Excel worksheet.
We will start by using the same code as in my last post to create a new workbook, worksheet, and then add some sample data to it.

using (var package = new ExcelPackage())
{
var workbook = package.Workbook;
var worksheet = workbook.Worksheets.Add("Sheet1");

worksheet.Cells["C1"].Value = "Widgets";
worksheet.Cells["C2"].Value = 20;
worksheet.Cells["C3"].Value = 5;
worksheet.Cells["C4"].Value = 30;
worksheet.Cells["C5"].Value = 32;
worksheet.Cells["C6"].Value = 17;
           
worksheet.Cells["B2"].Value = "Jan";
worksheet.Cells["B3"].Value = "Feb";
worksheet.Cells["B4"].Value = "Mar";
worksheet.Cells["B5"].Value = "Apr";
worksheet.Cells["B6"].Value = "May";

In this sample data we have labels in column B and values in column C. The first step in creating the chart is to create a new chart object in our worksheet, we do this with the AddChart function:

var chart = worksheet.Drawings.AddChart("chart", eChartType.ColumnStacked);

AddChart takes two parameters, the first is a name for the chart and the second is the type of chart. EPPlus supports a large number of different chart types, in this case we will use a stacked column (bar) chart. Next we need to add a data series to our chart:

var series = chart.Series.Add(“C2:C6", "B2:B6");           

The Series.Add function takes two parameters. The first specifies a range of cells that will be used for the values in the series, so we set this to C2:C6 which contains our numeric data. We skip C1 since it contains the column label, we will come back to this in a minute. The second parameter specifies a range of cells that contain the labels for our X-Axis, so we point this to B2-B6 which contains our month names.

If we were to run this now the series would end up with a default name in the legend of the chart. We can fix this using this line:

series.HeaderAddress = new ExcelAddress("'Sheet1'!C1");

With the HeaderAddress property of the series we can set which cell we want the name of the series to come from. Note that for this to work you must specify the full address of the cell including the sheet name. If you leave out the sheet name the name of the series will remain blank.

Since this is a stacked column chart we can add more then one series. If we had additional numeric data in column D we could create a second series like this:


var series2 = chart.Series.Add("D2:D6", "B2:B6");               
series2.HeaderAddress = new ExcelAddress("'Sheet1'!D1");

One thing that is missing from EPPlus is the ability to change the style (color, etc.) of a series, you have to stay with the default styles.

The final step is to save the workbook:

package.SaveAs(new System.IO.FileInfo(@"c:\temp\demoOut.xlsx"));

If you run this you will get the following results:

image