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
    ));
}
\$\endgroup\$
2
  • \$\begingroup\$ I would use a StringBuilder if this was for Java. There might be a C# equivalent? \$\endgroup\$
    – Jeremy
    Jan 19, 2011 at 23:02
  • \$\begingroup\$ @Jeremy Heiler, There is :) the exact same structure as well I believe \$\endgroup\$
    – RobertPitt
    Jan 19, 2011 at 23:37

5 Answers 5

19
\$\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.

\$\endgroup\$
2
  • \$\begingroup\$ This is a much cleaner method and addresses the error prone format string. This made my life easier. \$\endgroup\$ 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
    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();
\$\endgroup\$
2
  • 1
    \$\begingroup\$ I wonder if this is what String.Format uses under the hood? \$\endgroup\$
    – Jeremy
    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
    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.

\$\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\$
    – sepp2k
    Jan 20, 2011 at 4:40
  • \$\begingroup\$ @sepp2k Your right, I change the code to reflect that \$\endgroup\$
    – Sean Lynch
    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.

\$\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";
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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