23
\$\begingroup\$

I'm generating CSV strings for various 3rd party utilities and this section of code gets repeated in many classes. Is there a better way to generate this string?

public override string CsvString()
{
 return (
 string.Format("\u0022{0}\u0022,\u0022{1}\u0022,\u0022{2}\u0022,\u0022{3}\u0022,\u0022{4}\u0022,\u0022{5}\u0022,\u0022{6}\u0022,\u0022{7}\u0022,\u0022{8}\u0022,\u0022{9}\u0022,\u0022{10}\u0022,\u0022{11}\u0022,\u0022{12}\u0022,\u0022{13}\u0022",
 this.BlockType, // 1, A_NAME
 this.Tag, // 2, A_TAG
 this.Description, // 3, A_DESC
 this.InitialScan, // 4, A_ISCAN
 this.AutoManual, // 5, A_SCAN
 this.ScanTime, // 6, A_SCANT
 this.IoDevice, // 7, A_IODV
 this.IoAddress, // 8, A_IOAD
 this.InitialAmStatus, // 9, A_IAM
 this.AlarmPriority, // 10, A_PRI
 this.AlarmEnable, // 11, A_ENAB
 this.EnableOutput, // 12, A_EOUT
 this.HistDescription, // 13, A_HIST_DESC
 this.SecurityArea1 // 14, A_SECURITYAREA1
 ));
}
Jamal
35.2k13 gold badges134 silver badges238 bronze badges
asked Jan 19, 2011 at 22:56
\$\endgroup\$
2
  • \$\begingroup\$ I would use a StringBuilder if this was for Java. There might be a C# equivalent? \$\endgroup\$ Commented Jan 19, 2011 at 23:02
  • \$\begingroup\$ @Jeremy Heiler, There is :) the exact same structure as well I believe \$\endgroup\$ Commented Jan 19, 2011 at 23:37

5 Answers 5

18
\$\begingroup\$

I doubt you'll find a way of not listing all those properties without using reflection, but the following helps to eliminate that huge format string which is likely to become the source of bugs.

var properties = new Object[]
{
 this.BlockType, // 1, A_NAME
 this.Tag, // 2, A_TAG
 this.Description, // 3, A_DESC
 this.InitialScan, // 4, A_ISCAN
 this.AutoManual, // 5, A_SCAN
 this.ScanTime, // 6, A_SCANT
 this.IoDevice, // 7, A_IODV
 this.IoAddress, // 8, A_IOAD
 this.InitialAmStatus, // 9, A_IAM
 this.AlarmPriority, // 10, A_PRI
 this.AlarmEnable, // 11, A_ENAB
 this.EnableOutput, // 12, A_EOUT
 this.HistDescription, // 13, A_HIST_DESC
 this.SecurityArea1 // 14, A_SECURITYAREA1
}.Select(x => String.Format("\u0022{0}\u0022", x));
return String.Join(",", properties);

A couple of things to note:

This is hardly an efficient way of doing it, but offers fairly maintainable code. If you have an extra property, just add it to the array.

This will only work in .NET 4.0. In earlier versions, you'll have to call ToArray() after that call to Select.

answered Jan 20, 2011 at 7:56
\$\endgroup\$
2
  • \$\begingroup\$ This is a much cleaner method and addresses the error prone format string. This made my life easier. \$\endgroup\$ Commented Jan 20, 2011 at 15:15
  • 2
    \$\begingroup\$ If you do return "\u0022" + String.Join("\u0022,\u0022", properties) + "\u0022", you can avoid the need for the Select projection altogether. \$\endgroup\$ Commented Jan 27, 2011 at 21:30
10
\$\begingroup\$

Use a StringBuilder:

sbuilder.AppendFormat("\u0022{0}\u0022,\u0022{1}\u0022,\u0022{2}\u0022,\u0022{3}\u0022,\u0022{4}\u0022,\u0022{5}\u0022,\u0022{6}\u0022,\u0022{7}\u0022,\u0022{8}\u0022,\u0022{9}\u0022,\u0022{10}\u0022,\u0022{11}\u0022,\u0022{12}\u0022,\u0022{13}\u0022",
 this.BlockType, // 1, A_NAME
 this.Tag, // 2, A_TAG
 this.Description, // 3, A_DESC
 this.InitialScan, // 4, A_ISCAN
 this.AutoManual, // 5, A_SCAN
 this.ScanTime, // 6, A_SCANT
 this.IoDevice, // 7, A_IODV
 this.IoAddress, // 8, A_IOAD
 this.InitialAmStatus, // 9, A_IAM
 this.AlarmPriority, // 10, A_PRI
 this.AlarmEnable, // 11, A_ENAB
 this.EnableOutput, // 12, A_EOUT
 this.HistDescription, // 13, A_HIST_DESC
 this.SecurityArea1 // 14, A_SECURITYAREA1
 ).AppendLine();
answered Jan 19, 2011 at 23:10
\$\endgroup\$
2
  • 1
    \$\begingroup\$ I wonder if this is what String.Format uses under the hood? \$\endgroup\$ Commented Jan 19, 2011 at 23:40
  • 1
    \$\begingroup\$ Actually string.Format uses StringBuilder inside it (or so Reflector says), so there wouldn't memory/performance benefit in this specific case (one shot formatting of the string) \$\endgroup\$ Commented Jan 20, 2011 at 1:36
6
\$\begingroup\$

Maybe something like this:

public static string MakeCsvLine(params string[] items)
{
 return String.Format("\u0022{0}\u0022",String.Join("\u0022,\u0022",items));
}

Edit:

On thinking about it might be better to use it to build up a string builder so:

public static void AddCsvLine(StringBuilder sb, params string[] items)
{
 sb.AppendFormat("\u0022{0}\u0022",String.Join("\u0022,\u0022",items))
 .AppendLine();
}

This would remove having to have long strings repeated all over the code. Edit: Made the funtion return the orginal result.

answered Jan 20, 2011 at 3:00
\$\endgroup\$
2
  • 1
    \$\begingroup\$ You'd have to join on "\u0022,\u0022" and then also add "\u0022" at the beginning and end to get the same result as the original. \$\endgroup\$ Commented Jan 20, 2011 at 4:40
  • \$\begingroup\$ @sepp2k Your right, I change the code to reflect that \$\endgroup\$ Commented Jan 20, 2011 at 11:59
2
\$\begingroup\$

As a variant of Alex Humphrey's solution, You could try this for improved performance:

var properties = new Object[]
{
 this.BlockType, // 1, A_NAME
 this.Tag, // 2, A_TAG
 this.Description, // 3, A_DESC
 this.InitialScan, // 4, A_ISCAN
 this.AutoManual, // 5, A_SCAN
 this.ScanTime, // 6, A_SCANT
 this.IoDevice, // 7, A_IODV
 this.IoAddress, // 8, A_IOAD
 this.InitialAmStatus, // 9, A_IAM
 this.AlarmPriority, // 10, A_PRI
 this.AlarmEnable, // 11, A_ENAB
 this.EnableOutput, // 12, A_EOUT
 this.HistDescription, // 13, A_HIST_DESC
 this.SecurityArea1 // 14, A_SECURITYAREA1
};
var builder = new StringBuilder(properties.Length * 6);
foreach (var property in properties)
{
 builder.Append('"').Append(property).Append('"').Append(',');
}
builder.Remove(builder.Length - 1, 1); // remove the last comma
return builder.ToString();

But beware that this code is susceptible for failure if any of the properties contains a double quote. You should make sure they are escaped.

answered Oct 10, 2011 at 7:32
\$\endgroup\$
1
\$\begingroup\$

in the new versions of C# you can use string interpolation to make it a little easier to code and know where the variables are going to be inserted into the string. all you do is put a $ before the opening quotation mark.

Then the Return becomes this instead

return $"\u0022{this.BlockType}\u0022,\u0022{this.Tag}\u0022,\u0022{this.Description}\u0022,\u0022{This.InitialScan}\u0022,\u0022{this.AutoManual}\u0022,\u0022{this.ScanTime}\u0022,\u0022{this.IoDevice}\u0022,\u0022{this.IoAddress}\u0022,\u0022{this.InitialAmStatus}\u0022,\u0022{this.AlarmPriority}\u0022,\u0022{this.AlarmEnable}\u0022,\u0022{this.EnableOutput}\u0022,\u0022{this.HistDescription}\u0022,\u0022{this.SecurityArea}\u0022";
answered Dec 20, 2016 at 19:37
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.