Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type.
The main advantage of the extension method is to add new methods in the existing class without using inheritance. You can add new methods in the existing class without modifying the source code of the existing class. It can also work with sealed class.
This is one that's been getting some play from me lately:
public static IDisposable Tag(this HtmlHelper html, string tagName)
{
if (html == null)
throw new ArgumentNullException("html");
Action<string> a = tag => html.Write(String.Format(tag, tagName));
a("<{0}>");
return new Memento(() => a("</{0}>"));
}
Used like:
using (Html.Tag("ul"))
{
this.Model.ForEach(item => using(Html.Tag("li")) Html.Write(item));
using(Html.Tag("li")) Html.Write("new");
}
Memento is a handy class:
public sealed class Memento : IDisposable
{
private bool Disposed { get; set; }
private Action Action { get; set; }
public Memento(Action action)
{
if (action == null)
throw new ArgumentNullException("action");
Action = action;
}
void IDisposable.Dispose()
{
if (Disposed)
throw new ObjectDisposedException("Memento");
Disposed = true;
Action();
}
}
And to complete the dependencies:
public static void Write(this HtmlHelper html, string content)
{
if (html == null)
throw new ArgumentNullException("html");
html.ViewContext.HttpContext.Response.Write(content);
}
The full solution is too large to put here, but I wrote a series of extension methods that would allow you to easily convert a DataTable into a CSV.
public static String ToCSV(this DataTable dataTable)
{
return dataTable.ToCSV(null, COMMA, true);
}
public static String ToCSV(this DataTable dataTable, String qualifier)
{
return dataTable.ToCSV(qualifier, COMMA, true);
}
private static String ToCSV(this DataTable dataTable, String qualifier, String delimiter, Boolean includeColumnNames)
{
if (dataTable == null) return null;
if (qualifier == delimiter)
{
throw new InvalidOperationException(
"The qualifier and the delimiter are identical. This will cause the CSV to have collisions that might result in data being parsed incorrectly by another program.");
}
var sbCSV = new StringBuilder();
var delimiterToUse = delimiter ?? COMMA;
if (includeColumnNames)
sbCSV.AppendLine(dataTable.Columns.GetHeaderLine(qualifier, delimiterToUse));
foreach (DataRow row in dataTable.Rows)
{
sbCSV.AppendLine(row.ToCSVLine(qualifier, delimiterToUse));
}
return sbCSV.Length > 0 ? sbCSV.ToString() : null;
}
private static String ToCSVLine(this DataRow dataRow, String qualifier, String delimiter)
{
var colCount = dataRow.Table.Columns.Count;
var rowValues = new String[colCount];
for (var i = 0; i < colCount; i++)
{
rowValues[i] = dataRow[i].Qualify(qualifier);
}
return String.Join(delimiter, rowValues);
}
private static String GetHeaderLine(this DataColumnCollection columns, String qualifier, String delimiter)
{
var colCount = columns.Count;
var colNames = new String[colCount];
for (var i = 0; i < colCount; i++)
{
colNames[i] = columns[i].ColumnName.Qualify(qualifier);
}
return String.Join(delimiter, colNames);
}
private static String Qualify(this Object target, String qualifier)
{
return qualifier + target + qualifier;
}
At the end of the day, you could call it like this:
someDataTable.ToCSV(); //Plain old CSV
someDataTable.ToCSV("\""); //Double quote qualifier
someDataTable.ToCSV("\"", "\t"); //Tab delimited
I'm not a fan of the INotifyPropertyChanged
interface requiring that property names are passed as strings. I want a strongly-typed way to check at compile time that I'm only raising and handling property changes for properties that exist. I use this code to do that:
public static class INotifyPropertyChangedExtensions
{
public static string ToPropertyName<T>(this Expression<Func<T>> @this)
{
var @return = string.Empty;
if (@this != null)
{
var memberExpression = @this.Body as MemberExpression;
if (memberExpression != null)
{
@return = memberExpression.Member.Name;
}
}
return @return;
}
}
In classes that implement INotifyPropertyChanged
I include this helper method:
protected void NotifySetProperty<T>(ref T field, T value,
Expression<Func<T>> propertyExpression)
{
if (field == null ? value != null : !field.Equals(value))
{
field = value;
this.NotifyPropertyChanged(propertyExpression.ToPropertyName());
}
}
So that finally I can do this kind of thing:
private string _name;
public string Name
{
get { return _name; }
set { this.NotifySetProperty(ref _name, value, () => this.Name); }
}
It's strongly-typed and I only raise events for properties that actually change their value.
Well this isn't exactly clever but I've modified the ----OrDefault methods so you could specify a default item inline instead of checking for null later in your code:
public static T SingleOrDefault<T> ( this IEnumerable<T> source,
Func<T, bool> action, T theDefault )
{
T item = source.SingleOrDefault<T>(action);
if (item != null)
return item;
return theDefault;
}
Its incredible simple but really helps clean up those null checks. Best used when your UI is expecting a list of X items, like a tournament system, or game player slots and you want to display "empty seats".
Usage:
return jediList.SingleOrDefault(
j => j.LightsaberColor == "Orange",
new Jedi() { LightsaberColor = "Orange", Name = "DarthNobody");
Two that I like to use are the InsertWhere<T
> and RemoveWhere<T
> Extension Methods that I've written. Working with ObservableCollections in WPF and Silverlight I often need to modify ordered lists without recreating them. These methods allow me to insert and remove according to a supplied Func, so .OrderBy() doesn't need to be re-called.
/// <summary>
/// Removes all items from the provided <paramref name="list"/> that match the<paramref name="predicate"/> expression.
/// </summary>
/// <typeparam name="T">The class type of the list items.</typeparam>
/// <param name="list">The list to remove items from.</param>
/// <param name="predicate">The predicate expression to test against.</param>
public static void RemoveWhere<T>(this IList<T> list, Func<T, bool> predicate)
{
T[] copy = new T[] { };
Array.Resize(ref copy, list.Count);
list.CopyTo(copy, 0);
for (int i = copy.Length - 1; i >= 0; i--)
{
if (predicate(copy[i]))
{
list.RemoveAt(i);
}
}
}
/// <summary>
/// Inserts an Item into a list at the first place that the <paramref name="predicate"/> expression fails. If it is true in all cases, then the item is appended to the end of the list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="obj"></param>
/// <param name="predicate">The sepcified function that determines when the <paramref name="obj"/> should be added. </param>
public static void InsertWhere<T>(this IList<T> list, T obj, Func<T, bool> predicate)
{
for (int i = 0; i < list.Count; i++)
{
// When the function first fails it inserts the obj paramiter.
// For example, in a list myList of ordered Int32's {1,2,3,4,5,10,12}
// Calling myList.InsertWhere( 8, x => 8 > x) inserts 8 once the list item becomes greater then or equal to it.
if(!predicate(list[i]))
{
list.Insert(i, obj);
return;
}
}
list.Add(obj);
}
Edit:
Talljoe made some significant improvements to the RemoveWhere/RemoveAll, that I had hastily constructed. With ~3mill items removing every third one the new version takes only ~50 milliseconds (less then 10 if it can call List.RemoveAll !) as opposed to the RemoveWhere 's multiple seconds (I got tired of waiting for it.)
Here is his greatly improved version, thanks again!
public static void RemoveAll<T>(this IList<T> instance, Predicate<T> predicate)
{
if (instance == null)
throw new ArgumentNullException("instance");
if (predicate == null)
throw new ArgumentNullException("predicate");
if (instance is T[])
throw new NotSupportedException();
var list = instance as List<T>;
if (list != null)
{
list.RemoveAll(predicate);
return;
}
int writeIndex = 0;
for (int readIndex = 0; readIndex < instance.Count; readIndex++)
{
var item = instance[readIndex];
if (predicate(item)) continue;
if (readIndex != writeIndex)
{
instance[writeIndex] = item;
}
++writeIndex;
}
if (writeIndex != instance.Count)
{
for (int deleteIndex = instance.Count - 1; deleteIndex >= writeIndex; --deleteIndex)
{
instance.RemoveAt(deleteIndex);
}
}
}
I have various .Debugify
extension methods that are useful for dumping objects to a log file. For example, here's my Dictionary debugify (I have these for List, Datatable, param array, etc.):
public static string Debugify<TKey, TValue>(this Dictionary<TKey, TValue> dictionary) {
string Result = "";
if (dictionary.Count > 0) {
StringBuilder ResultBuilder = new StringBuilder();
int Counter = 0;
foreach (KeyValuePair<TKey, TValue> Entry in dictionary) {
Counter++;
ResultBuilder.AppendFormat("{0}: {1}, ", Entry.Key, Entry.Value);
if (Counter % 10 == 0) ResultBuilder.AppendLine();
}
Result = ResultBuilder.ToString();
}
return Result;
}
And here's one for a DbParameterCollection (useful for dumping database calls to the log file):
public static string Debugify(this DbParameterCollection parameters) {
List<string> ParameterValuesList = new List<string>();
foreach (DbParameter Parameter in parameters) {
string ParameterName, ParameterValue;
ParameterName = Parameter.ParameterName;
if (Parameter.Direction == ParameterDirection.ReturnValue)
continue;
if (Parameter.Value == null || Parameter.Value.Equals(DBNull.Value))
ParameterValue = "NULL";
else
{
switch (Parameter.DbType)
{
case DbType.String:
case DbType.Date:
case DbType.DateTime:
case DbType.Guid:
case DbType.Xml:
ParameterValue
= "'" + Parameter
.Value
.ToString()
.Replace(Environment.NewLine, "")
.Left(80, "...") + "'"; // Left... is another nice one
break;
default:
ParameterValue = Parameter.Value.ToString();
break;
}
if (Parameter.Direction != ParameterDirection.Input)
ParameterValue += " " + Parameter.Direction.ToString();
}
ParameterValuesList.Add(string.Format("{0}={1}", ParameterName, ParameterValue));
}
return string.Join(", ", ParameterValuesList.ToArray());
}
Example result:
Log.DebugFormat("EXEC {0} {1}", procName, params.Debugify);
// EXEC spProcedure @intID=5, @nvName='Michael Haren', @intRefID=11 OUTPUT
Note that if you call this after your DB calls, you'll get the output parameters filled in, too. I call this on a line that includes the SP name so I can copy/paste the call into SSMS for debugging.
These make my log files pretty and easy to generate without interrupting my code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With