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
));
}
-
\$\begingroup\$ I would use a StringBuilder if this was for Java. There might be a C# equivalent? \$\endgroup\$Jeremy– Jeremy2011年01月19日 23:02:53 +00:00Commented Jan 19, 2011 at 23:02
-
\$\begingroup\$ @Jeremy Heiler, There is :) the exact same structure as well I believe \$\endgroup\$RobertPitt– RobertPitt2011年01月19日 23:37:37 +00:00Commented Jan 19, 2011 at 23:37
5 Answers 5
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
.
-
\$\begingroup\$ This is a much cleaner method and addresses the error prone format string. This made my life easier. \$\endgroup\$Greg Buehler– Greg Buehler2011年01月20日 15:15:50 +00:00Commented 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\$ICR– ICR2011年01月27日 21:30:33 +00:00Commented Jan 27, 2011 at 21:30
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();
-
1\$\begingroup\$ I wonder if this is what
String.Format
uses under the hood? \$\endgroup\$Jeremy– Jeremy2011年01月19日 23:40:07 +00:00Commented 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\$Jaime– Jaime2011年01月20日 01:36:26 +00:00Commented Jan 20, 2011 at 1:36
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.
-
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\$sepp2k– sepp2k2011年01月20日 04:40:23 +00:00Commented Jan 20, 2011 at 4:40 -
\$\begingroup\$ @sepp2k Your right, I change the code to reflect that \$\endgroup\$Sean Lynch– Sean Lynch2011年01月20日 11:59:52 +00:00Commented Jan 20, 2011 at 11:59
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.
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";