我开始非常喜欢延伸方法......我想知道是否有人偶然发现了一个真正引起他们注意的人,或者只是发现了聪明.
我今天写的一个例子:
由于其他用户的评论而编辑:
public static IEnumerableTo(this int fromNumber, int toNumber) { while (fromNumber < toNumber) { yield return fromNumber; fromNumber++; } }
这允许将for循环写为foreach循环:
foreach (int x in 0.To(16)) { Console.WriteLine(Math.Pow(2, x).ToString()); }
我迫不及待想看其他例子!请享用!
完整的解决方案太大了,不能放在这里,但我写了一系列扩展方法,可以让你轻松地将DataTable转换为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; }
在一天结束时,您可以这样称呼它:
someDataTable.ToCSV(); //Plain old CSV someDataTable.ToCSV("\""); //Double quote qualifier someDataTable.ToCSV("\"", "\t"); //Tab delimited
这是最近一直在玩我的游戏:
public static IDisposable Tag(this HtmlHelper html, string tagName) { if (html == null) throw new ArgumentNullException("html"); Actiona = tag => html.Write(String.Format(tag, tagName)); a("<{0}>"); return new Memento(() => a("{0}>")); }
使用如下:
using (Html.Tag("ul")) { this.Model.ForEach(item => using(Html.Tag("li")) Html.Write(item)); using(Html.Tag("li")) Html.Write("new"); }
纪念品是一个方便的课程:
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(); } }
并完成依赖:
public static void Write(this HtmlHelper html, string content) { if (html == null) throw new ArgumentNullException("html"); html.ViewContext.HttpContext.Response.Write(content); }
我不是INotifyPropertyChanged
要求属性名称作为字符串传递的接口的粉丝.我想要一种强类型的方法在编译时检查我只是为现有的属性提出并处理属性更改.我使用这段代码来做到这一点:
public static class INotifyPropertyChangedExtensions { public static string ToPropertyName(this Expression > @this) { var @return = string.Empty; if (@this != null) { var memberExpression = @this.Body as MemberExpression; if (memberExpression != null) { @return = memberExpression.Member.Name; } } return @return; } }
在实现INotifyPropertyChanged
我的类中包含这个帮助器方法:
protected void NotifySetProperty(ref T field, T value, Expression > propertyExpression) { if (field == null ? value != null : !field.Equals(value)) { field = value; this.NotifyPropertyChanged(propertyExpression.ToPropertyName()); } }
所以最后我可以做这样的事情:
private string _name; public string Name { get { return _name; } set { this.NotifySetProperty(ref _name, value, () => this.Name); } }
它是强类型的,我只为实际改变其值的属性引发事件.
嗯,这不是很聪明,但我修改了---- OrDefault方法,因此您可以指定一个默认的内联项,而不是在代码中稍后检查null:
public static T SingleOrDefault( this IEnumerable source, Func action, T theDefault ) { T item = source.SingleOrDefault (action); if (item != null) return item; return theDefault; }
它令人难以置信的简单,但真的有助于清理那些空检查.当您的UI期望X项目列表(如锦标赛系统或游戏玩家插槽)并且您希望显示"空座位"时,最佳使用.
用法:
return jediList.SingleOrDefault( j => j.LightsaberColor == "Orange", new Jedi() { LightsaberColor = "Orange", Name = "DarthNobody");
这是我一起砍的一个,所以随意挑选它.它采用(有序)整数列表并返回连续范围的字符串列表.例如:
1,2,3,7,10,11,12 --> "1-3","7","10-12"
该函数(在静态类中):
public static IEnumerableIntRanges(this IEnumerable numbers) { int rangeStart = 0; int previous = 0; if (!numbers.Any()) yield break; rangeStart = previous = numbers.FirstOrDefault(); foreach (int n in numbers.Skip(1)) { if (n - previous > 1) // sequence break - yield a sequence { if (previous > rangeStart) { yield return string.Format("{0}-{1}", rangeStart, previous); } else { yield return rangeStart.ToString(); } rangeStart = n; } previous = n; } if (previous > rangeStart) { yield return string.Format("{0}-{1}", rangeStart, previous); } else { yield return rangeStart.ToString(); } }
用法示例:
this.WeekDescription = string.Join(",", from.WeekPattern.WeekPatternToInts().IntRanges().ToArray());
此代码用于转换DailyWTF值得的时间表应用程序中的数据.WeekPattern是存储在字符串"0011011100 ..."中的位掩码.WeekPatternToInts()将其转换为IEnumerable
我喜欢使用的两个是我编写的InsertWhere
////// Removes all items from the provided ///that match the expression. /// The class type of the list items. /// The list to remove items from. /// The predicate expression to test against. public static void RemoveWhere(this IList list, Func 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); } } } /// /// Inserts an Item into a list at the first place that the ///expression fails. If it is true in all cases, then the item is appended to the end of the list. /// /// /// /// The sepcified function that determines when the should be added. public static void InsertWhere (this IList list, T obj, Func 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); }
编辑:
Talljoe对RemoveWhere/RemoveAll进行了一些重大改进,我已经匆忙构建了.每三个项目删除大约3个项目,新版本只需要约50毫秒(如果它可以调用List.RemoveAll,则少于10个!)而不是RemoveWhere的多秒(我厌倦了等待它.)
这是他大大改进的版本,再次感谢!
public static void RemoveAll(this IList instance, Predicate 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 ; 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); } } }
我有各种.Debugify
扩展方法,可用于将对象转储到日志文件.例如,这是我的字典调试(我有这些用于List,Datatable,param数组等):
public static string Debugify(this Dictionary dictionary) { string Result = ""; if (dictionary.Count > 0) { StringBuilder ResultBuilder = new StringBuilder(); int Counter = 0; foreach (KeyValuePair Entry in dictionary) { Counter++; ResultBuilder.AppendFormat("{0}: {1}, ", Entry.Key, Entry.Value); if (Counter % 10 == 0) ResultBuilder.AppendLine(); } Result = ResultBuilder.ToString(); } return Result; }
这里有一个DbParameterCollection(用于将数据库调用转储到日志文件):
public static string Debugify(this DbParameterCollection parameters) { ListParameterValuesList = new List (); 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()); }
示例结果:
Log.DebugFormat("EXEC {0} {1}", procName, params.Debugify); // EXEC spProcedure @intID=5, @nvName='Michael Haren', @intRefID=11 OUTPUT
请注意,如果在数据库调用后调用此方法,您也将获得输出参数.我在一个包含SP名称的行上调用它,这样我就可以将调用复制/粘贴到SSMS中进行调试.
这些使我的日志文件非常容易生成而不会中断我的代码.
一对扩展方法,用于将base-36字符串(!)转换为整数:
public static int ToBase10(this string base36) { if (string.IsNullOrEmpty(base36)) return 0; int value = 0; foreach (var c in base36.Trim()) { value = value * 36 + c.ToBase10(); } return value; } public static int ToBase10(this char c) { if (c >= '0' && c <= '9') return c - '0'; c = char.ToUpper(c); if (c >= 'A' && c <= 'Z') return c - 'A' + 10; return 0; }
(有些天才决定在数据库中存储数字的最佳方法是将它们编码为字符串.小数点占用太多空间.十六进制更好,但不使用字符GZ.所以显然你将base-16扩展到base-36! )
我编写了一系列扩展方法,以便更容易地操作ADO.NET对象和方法:
在一条指令中从DbConnection创建DbCommand:
public static DbCommand CreateCommand(this DbConnection connection, string commandText) { DbCommand command = connection.CreateCommand(); command.CommandText = commandText; return command; }
将参数添加到DbCommand:
public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType) { DbParameter p = AddParameter(command, name, dbType, 0, ParameterDirection.Input); return p; } public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, object value) { DbParameter p = AddParameter(command, name, dbType, 0, ParameterDirection.Input); p.Value = value; return p; } public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, int size) { return AddParameter(command, name, dbType, size, ParameterDirection.Input); } public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, int size, ParameterDirection direction) { DbParameter parameter = command.CreateParameter(); parameter.ParameterName = name; parameter.DbType = dbType; parameter.Direction = direction; parameter.Size = size; command.Parameters.Add(parameter); return parameter; }
按名称而不是索引访问DbDataReader字段:
public static DateTime GetDateTime(this DbDataReader reader, string name) { int i = reader.GetOrdinal(name); return reader.GetDateTime(i); } public static decimal GetDecimal(this DbDataReader reader, string name) { int i = reader.GetOrdinal(name); return reader.GetDecimal(i); } public static double GetDouble(this DbDataReader reader, string name) { int i = reader.GetOrdinal(name); return reader.GetDouble(i); } public static string GetString(this DbDataReader reader, string name) { int i = reader.GetOrdinal(name); return reader.GetString(i); } ...
另一个(不相关的)扩展方法允许我在WinForms表单和控件上执行DragMove操作(如在WPF中),请参见此处.
我在这里看到的扩展方法的大多数示例都违背了最佳实践.扩展方法功能强大,但应谨慎使用.根据我的经验,具有旧式语法的静态助手/实用程序类通常对于大多数这些是优选的.
对于Enums的扩展方法,有一些话要说,因为它们不可能有方法.如果您在与Enum相同的命名空间中定义它们并在同一个程序集中,它们将以透明方式工作.
虽然非常简单,但我发现这个特别有用,因为我从一个完整的结果集中获得了一个项目的100亿次页面:
public static class QueryableExtensions { public static IQueryablePage(this IQueryable query, int pageNumber, int pageSize) { int skipCount = (pageNumber-1) * pageSize; query = query.Skip(skipCount); query = query.Take(pageSize); return query; } }
这是一种在引发事件之前集中空检查的扩展方法.
public static class EventExtension { public static void RaiseEvent(this EventHandler handler, object obj, T args) where T : EventArgs { EventHandler theHandler = handler; if (theHandler != null) { theHandler(obj, args); } } }
通常,我需要根据Enum值显示用户友好的值,但不想使用自定义属性路由,因为它看起来不太优雅.
有了这个方便的扩展方法:
public static string EnumValue(this MyEnum e) { switch (e) { case MyEnum.First: return "First Friendly Value"; case MyEnum.Second: return "Second Friendly Value"; case MyEnum.Third: return "Third Friendly Value"; } return "Horrible Failure!!"; }
我可以做这个:
Console.WriteLine(MyEnum.First.EnumValue());
好极了!