Extension methods were added as a new compiler feature in .NET 3.5. More specifically, that means you can use VS 2008 to use an extension method and then use VS's multi-targeting to run it under .NET 2.0. They're basically a means of tacking on methods onto existing classes or interfaces w/o actually needed to subclass or modify an interface. It's used extensively by (and added because of) LINQ. The methods aren't really part of the class, but the way you use them (and the way they appear in intellisense) make them feel like they're now part of the class. They're essentially static methods scoped to a specific interface or class.
I've been playing around with them a bit and ran into a case where I thought they'd be kind of a cool fit. I've needed to be able to convert a datatable into a comma-delimited file (CSV) so it can easily be opening in something like Excel, or pretty much anything that understands CSV files. I could create a separate class to do this, but it seems like this should be part of the DataTable class. To write an extension method you basically create a static method in a static class and prefix the first parameter with "this". Yep, that's about it.
I wanted it to basically work like this:
Creating the CSV is pretty straightforward - I loop through the column headers to generate the first header row, then I loop through each row in the table, then each item in the ItemArray of the row. I specifically decided to use quotes as delimiters around everything to keep it simple - the rules as to when you can/should include quotes for a CSV are pretty complicated. The only thing I do is escape out embedded quotes in the data by doubling them, ex. " becomes "". As soon as I had it working, I decided to create a few more overloads to let me control whether a header row was required, and the actual delimiter used (ex. instead of comma you could change it to a | pipe for example). Their is some example code in the XML help at the top of the class. In addition, I'm actually using this for a web page so it might be helpful to see what that code looks like:
string results = act.DataSet.Tables[tableName].ToCSV();
string mimeType = RCSSolutions.Web.Utility.DetermineMimeType("csv");
Response.ContentType = mimeType;
Response.AddHeader("Content-Length", results.Length.ToString());
Response.AddHeader("Content-disposition",
string.Format("attachment;filename={0}", "DelimitedList.CSV"));
Response.Write(results);
Response.End();
I'm calling out to another helper class which returns the mime type - in this case, all it does is return "application/csv". The above code basically pops open a dialog box with the file name filled in the browser on the client side.
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
namespace RCSSolutions.Utility
{
/// <summary>
/// <para>Various extension methods.</para>
/// </summary>
/// Sample of using ToCSV
/// <example>
/// DataTable table = dv.Table;
/// // Assumes table is a DataTable
/// string result = table.ToCSV(true);
/// System.IO.File.WriteAllText(@"C:\sample.csv", result);
/// System.Diagnostics.Process proc = new System.Diagnostics.Process();
/// proc.StartInfo.FileName = @"C:\sample.csv";
/// proc.StartInfo.UseShellExecute = true;
/// proc.Start();
/// </example>
public static class Extensions
/// Converts the passed in data table to a CSV-style string.
/// <param name="table">Table to convert</param>
/// <returns>Resulting CSV-style string</returns>
public static string ToCSV(this DataTable table)
return ToCSV(table, ",", true);
}
/// <param name="includeHeader">true - include headers<br/>
/// false - do not include header column</param>
public static string ToCSV(this DataTable table, bool includeHeader)
return ToCSV(table, ",", includeHeader);
/// <param name="delimiter">Delimiter used to separate fields</param>
public static string ToCSV(this DataTable table, string delimiter, bool includeHeader)
StringBuilder result = new StringBuilder();
if (includeHeader)
foreach (DataColumn column in table.Columns)
result.Append(column.ColumnName);
result.Append(delimiter);
result.Remove(--result.Length, 0);
result.Append(Environment.NewLine);
foreach (DataRow row in table.Rows)
foreach (object item in row.ItemArray)
if (item is System.DBNull)
else
string itemAsString = item.ToString();
// Double up all embedded double quotes
itemAsString = itemAsString.Replace("\"", "\"\"");
// To keep things simple, always delimit with double-quotes
// so we don't have to determine in which cases they're necessary
// and which cases they're not.
itemAsString = "\"" + itemAsString + "\"";
result.Append(itemAsString + delimiter);
return result.ToString();
Remember Me
a@href@title, b, i, strike