您所见过的扩展方法的最佳或最有趣的用法是什么?

我开始真正喜欢扩展方法了... ... 我想知道是否有人她偶然发现了一个真正让他们大吃一惊的方法,或者只是发现了一个聪明的方法。

我今天写的一个例子:

根据其他用户的评论编辑:

public static IEnumerable<int> To(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());
}

我等不及要看其他的例子了! 好好享受吧!

11809 次浏览

我喜欢 这个。它是 String 的一个变体。拆分方法,当拆分字符应该位于实际字符串中时,允许使用转义字符来禁止拆分。

完整的解决方案太大,不能放在这里,但是我编写了一系列扩展方法,它们允许您轻松地将 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

这并不是很聪明,但是我已经修改了—— OrDefault 方法,这样您就可以在内联中指定一个默认项,而不必在以后的代码中检查 null:

    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;
}

它简单得令人难以置信,但确实有助于清除那些空检查。当你的用户界面需要一个 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 IEnumerable<string> IntRanges(this IEnumerable<int> 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 ()将其转换为 IEnumable < int > ,在本例中[3,4,6,7,8] ,它变成“3-4,6-8”。它为用户提供了一个演讲所涉及的学术周范围的简洁描述。

我喜欢使用的两个扩展方法是我编写的 InsertWHERE <T > 和 RemoveWHERE <T > 扩展方法。在 WPF 和 Silverlight 中使用 Observer ableCollection 时,我经常需要修改有序列表,而不需要重新创建它们。这些方法允许我根据提供的 Func 插入和删除,因此。OrderBy ()不需要重新调用。

    /// <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);
}

编辑:
Talljoe 对我匆忙构建的 RemoveWhere/RemoveAll 做了一些重大改进。如果每移除三分之一的项目就移除三分之一的项目,那么新版本只需要50毫秒(如果能够调用 List,则不到10毫秒)。全部移除!)相对于 RemoveWhere 的多秒(我已经厌倦了等待它。)

这里是他的大大改进版本,再次感谢!

    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);
}
}
}

下面是我最近在工作中写的一篇文章,并在博客中写道:

Http://crazorsharp.blogspot.com/2009/03/cool-ienumberable-extension-method_25.html

它基本上是 IEnumerable.ToHtmlTable () ;

方法对 int 进行扩展,以解码一个位掩码,该位掩码指定 DayOfWeek 枚举的天数(在本例中,一周的第一天为星期一) :

public static IEnumerable<DayOfWeek> Days(this int dayMask)
{
if ((dayMask & 1) > 0) yield return DayOfWeek.Monday;
if ((dayMask & 2) > 0) yield return DayOfWeek.Tuesday;
if ((dayMask & 4) > 0) yield return DayOfWeek.Wednesday;
if ((dayMask & 8) > 0) yield return DayOfWeek.Thursday;
if ((dayMask & 16) > 0) yield return DayOfWeek.Friday;
if ((dayMask & 32) > 0) yield return DayOfWeek.Saturday;
if ((dayMask & 64) > 0) yield return DayOfWeek.Sunday;
}

一对将 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;
}

(一些天才认为在数据库中存储数字的最好方法是将它们编码为字符串。Decimals 占地太大。十六进制更好,但不使用字符 G-Z。所以很明显你把16进制延伸到了36进制!)

我有各种 .Debugify扩展方法,这些方法对于将对象转储到日志文件非常有用。例如,下面是我的 Dictionary degify (我为 List、 Dattable、 param array 等提供了这些工具) :

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;
}

下面是一个 DbParameter terCollection (对于转储对日志文件的数据库调用很有用) :

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());
}

示例结果:

Log.DebugFormat("EXEC {0} {1}", procName, params.Debugify);
// EXEC spProcedure @intID=5, @nvName='Michael Haren', @intRefID=11 OUTPUT

请注意,如果将这个 之后调用为数据库调用,也会得到填充的输出参数。我在包含 SP 名称的行上调用这个函数,这样我就可以将调用复制/粘贴到 SSMS 中进行调试。


这使得我的日志文件很漂亮,很容易生成,而不会中断我的代码。

这个例子创建了一个数组,在最开始添加了一个元素:

public static T[] Prepend<T>(this T[] array, T item)
{
T[] result = new T[array.Length + 1];
result[0] = item;
Array.Copy(array, 0, result, 1, array.Length);
return result;
}


string[] some = new string[] { "foo", "bar" };
...
some = some.Prepend("baz");

当我需要把一些表达式转换成正方形时,这个可以帮助我:

public static double Sq(this double arg)
{
return arg * arg;
}


(x - x0).Sq() + (y - y0).Sq() + (z - z0).Sq()

这是我写的另一个故事:

    public static class StringExtensions
{
/// <summary>
/// Returns a Subset string starting at the specified start index and ending and the specified end
/// index.
/// </summary>
/// <param name="s">The string to retrieve the subset from.</param>
/// <param name="startIndex">The specified start index for the subset.</param>
/// <param name="endIndex">The specified end index for the subset.</param>
/// <returns>A Subset string starting at the specified start index and ending and the specified end
/// index.</returns>
public static string Subsetstring(this string s, int startIndex, int endIndex)
{
if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex", "Must be positive.");
if (endIndex < 0) throw new ArgumentOutOfRangeException("endIndex", "Must be positive.");
if (startIndex > endIndex) throw new ArgumentOutOfRangeException("endIndex", "Must be >= startIndex.");
return s.Substring(startIndex, (endIndex - startIndex));
}


/// <summary>
/// Finds the specified Start Text and the End Text in this string instance, and returns a string
/// containing all the text starting from startText, to the begining of endText. (endText is not
/// included.)
/// </summary>
/// <param name="s">The string to retrieve the subset from.</param>
/// <param name="startText">The Start Text to begin the Subset from.</param>
/// <param name="endText">The End Text to where the Subset goes to.</param>
/// <param name="ignoreCase">Whether or not to ignore case when comparing startText/endText to the string.</param>
/// <returns>A string containing all the text starting from startText, to the begining of endText.</returns>
public static string Subsetstring(this string s, string startText, string endText, bool ignoreCase)
{
if (string.IsNullOrEmpty(startText)) throw new ArgumentNullException("startText", "Must be filled.");
if (string.IsNullOrEmpty(endText)) throw new ArgumentNullException("endText", "Must be filled.");
string temp = s;
if (ignoreCase)
{
temp = s.ToUpperInvariant();
startText = startText.ToUpperInvariant();
endText = endText.ToUpperInvariant();
}
int start = temp.IndexOf(startText);
int end = temp.IndexOf(endText, start);
return Subsetstring(s, start, end);
}
}

这背后的动机很简单。它总是困扰着我,内置的 Substring 方法是如何将 startindex 和 length 作为参数的。做 startindex 和 endindex 总是更有帮助。所以我自己卷了:

用法:

        string s = "This is a tester for my cool extension method!!";
s = s.Subsetstring("tester", "cool",true);

我必须使用 Subsetstring 的原因是因为 Substring 的重载已经接受了两个 int。如果有人有更好的名字,请告诉我! !

Format 不应该是静态的,所以我使用了一个名为 frmt 的扩展方法:

<Extension()> Public Function frmt(ByVal format As String,
ByVal ParamArray args() As Object) As String
If format Is Nothing Then Throw New ArgumentNullException("format")
Return String.Format(format, args)
End Function

当我想在不构造二进制写入器的情况下读取或写入一个字节流中的数字时(技术上讲,在用写入器包装原始流之后,你不应该修改它) :

<Extension()> Public Function Bytes(ByVal n As ULong,
ByVal byteOrder As ByteOrder,
Optional ByVal size As Integer = 8) As Byte()
Dim data As New List(Of Byte)
Do Until data.Count >= size
data.Add(CByte(n And CULng(&HFF)))
n >>= 8
Loop
Select Case byteOrder
Case ByteOrder.BigEndian
Return data.ToArray.reversed
Case ByteOrder.LittleEndian
Return data.ToArray
Case Else
Throw New ArgumentException("Unrecognized byte order.")
End Select
End Function
<Extension()> Public Function ToULong(ByVal data As IEnumerable(Of Byte),
ByVal byteOrder As ByteOrder) As ULong
If data Is Nothing Then Throw New ArgumentNullException("data")
Dim val As ULong
Select Case byteOrder
Case ByteOrder.LittleEndian
data = data.Reverse
Case ByteOrder.BigEndian
'no change required
Case Else
Throw New ArgumentException("Unrecognized byte order.")
End Select
For Each b In data
val <<= 8
val = val Or b
Next b
Return val
End Function

这是我最近玩的一个游戏:

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}>"));
}

用法如下:

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);
}

酷,也爱延长!

这里有一些。

这个会得到每月最后一次约会:

<System.Runtime.CompilerServices.Extension()> _
Public Function GetLastMonthDay(ByVal Source As DateTime) As DateTime
Dim CurrentMonth As Integer = Source.Month
Dim MonthCounter As Integer = Source.Month
Dim LastDay As DateTime
Dim DateCounter As DateTime = Source


LastDay = Source


Do While MonthCounter = CurrentMonth
DateCounter = DateCounter.AddDays(1)
MonthCounter = DateCounter.Month


If MonthCounter = CurrentMonth Then
LastDay = DateCounter
End If
Loop


Return LastDay
End Function

这两点让反思变得容易一些:

 <System.Runtime.CompilerServices.Extension()> _
Public Function GetPropertyValue(Of ValueType)(ByVal Source As Object, ByVal PropertyName As String) As ValueType
Dim pInfo As System.Reflection.PropertyInfo


pInfo = Source.GetType.GetProperty(PropertyName)


If pInfo Is Nothing Then
Throw New Exception("Property " & PropertyName & " does not exists for object of type " & Source.GetType.Name)
Else
Return pInfo.GetValue(Source, Nothing)
End If
End Function


<System.Runtime.CompilerServices.Extension()> _
Public Function GetPropertyType(ByVal Source As Object, ByVal PropertyName As String) As Type
Dim pInfo As System.Reflection.PropertyInfo


pInfo = Source.GetType.GetProperty(PropertyName)


If pInfo Is Nothing Then
Throw New Exception("Property " & PropertyName & " does not exists for object of type " & Source.GetType.Name)
Else
Return pInfo.PropertyType
End If
End Function

这一个移动序列,以便您得到给定的项目第一。例如,我使用它来取周的第一天,并将其移位,以便序列中的第一天是当前文化的周的第一天。

    /// <summary>
/// Shifts a sequence so that the given <paramref name="item"/> becomes the first.
/// Uses the specified equality <paramref name="comparer"/> to find the item.
/// </summary>
/// <typeparam name="TSource">Type of elements in <paramref name="source"/>.</typeparam>
/// <param name="source">Sequence of elements.</param>
/// <param name="item">Item which will become the first.</param>
/// <param name="comparer">Used to find the first item.</param>
/// <returns>A shifted sequence. For example Shift({1,2,3,4,5,6}, 3) would become {3,4,5,6,1,2}. </returns>
public static IEnumerable<TSource> Shift<TSource>(this IEnumerable<TSource> source, TSource item, IEqualityComparer<TSource> comparer)
{
var queue = new Queue<TSource>();
bool found = false;


foreach (TSource e in source)
{
if (!found && comparer.Equals(item, e))
found = true;


if (found)
yield return e;
else
queue.Enqueue(e);
}


while (queue.Count > 0)
yield return queue.Dequeue();
}




/// <summary>
/// Shifts a sequence so that the given item becomes the first.
/// Uses the default equality comparer to find the item.
/// </summary>
/// <typeparam name="TSource">Type of elements in <paramref name="source"/>.</typeparam>
/// <param name="source">Sequence of elements.</param>
/// <param name="element">Element which will become the first.</param>
/// <returns>A shifted sequence. For example Shift({1,2,3,4,5,6}, 3) would become {3,4,5,6,1,2}. </returns>
public static IEnumerable<TSource> Shift<TSource>(this IEnumerable<TSource> source, TSource element)
{
return Shift(source, element, EqualityComparer<TSource>.Default);
}

我使用最多的扩展方法必须是 System.Linq.Enumerable类中的方法。

莫林克中可以找到这个列表的一个很好的和有用的扩展。

我在这里看到的大多数扩展方法示例都违背了最佳实践。 扩展方法很强大,但是应该谨慎使用。根据我的经验,对于其中的大多数类来说,具有老式语法的静态助手/实用程序类通常是更好的选择。

对于 Enums 的扩展方法有一些要说的,因为它们不可能有方法。如果在与 Enum 相同的命名空间和相同的程序集中定义它们,则它们的工作是透明的。

我编写了一系列扩展方法,以使操作 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)。

我在这里提到了一些我使用的方法:

在 MVC 项目中,为了解决类名到大写的 url 问题,我自动导航以下代码:

public static class RouteCollectionExt
{
public static Route MapRouteLowercase(this RouteCollection routes, string name, string url, object defaults)
{
var route = new LowercaseRoute(url, new RouteValueDictionary(defaults), new MvcRouteHandler());


routes.Add(name, route);


return route;
}


private class LowercaseRoute : Route
{
public LowercaseRoute(string url, IRouteHandler routeHandler)
: base(url, routeHandler) { }


public LowercaseRoute(string url, RouteValueDictionary defaults, IRouteHandler routeHandler)
: base(url, defaults, routeHandler) { }


public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler)
: base(url, defaults, constraints, routeHandler) { }


public LowercaseRoute(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
: base(url, defaults, constraints, dataTokens, routeHandler) { }


public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
var path = base.GetVirtualPath(requestContext, values);


if (path != null)
{
path.VirtualPath = path.VirtualPath.ToLowerInvariant();
}


return path;
}
}
}

用法:

routes.MapRouteLowercase(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);

虽然很简单,但是我发现这个方法特别有用,因为我在一个项目中获得了一个完整结果集的页面,而这个结果集是一个项目的100亿倍:

public static class QueryableExtensions
{
public static IQueryable<T> Page(this IQueryable<T> query, int pageNumber, int pageSize)
{
int skipCount = (pageNumber-1) * pageSize;
query = query.Skip(skipCount);
query = query.Take(pageSize);


return query;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;


namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string guid1 = "936DA01F-9ABD-4d9d-80C7-02AF85C822A8";
string guid2 = "936DA01F-9ABD-4d9d-80C7-02AF85C822A";
Console.WriteLine("guid1: {0}", guid1.IsGuid());
Console.WriteLine("guid2: {0}", guid2.IsGuid());
Console.ReadLine();
}
}


public static class GuidUtility
{
/// <summary>
/// Determines if string is legitimate GUID
/// </summary>
public static Boolean IsGuid(this String s)
{
string pattern = @"^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$";
Regex regex = new Regex(pattern);
return regex.IsMatch(s);
}
}
}

几个扩展我大多使用。第一个集合是对象扩展,实际上只用于转换。

public static class ObjectExtension
{
public static T As<T>(this object value)
{
return (value != null && value is T) ? (T)value : default(T);
}


public static int AsInt(this string value)
{
if (value.HasValue())
{
int result;


var success = int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);


if (success)
{
return result;
}
}


return 0;
}


public static Guid AsGuid(this string value)
{
return value.HasValue() ? new Guid(value) : Guid.Empty;
}
}

字符串扩展名

public static class StringExtension
{
public static bool HasValue(this string value)
{
return string.IsNullOrEmpty(value) == false;
}


public static string Slug(this string value)
{
if (value.HasValue())
{
var builder = new StringBuilder();
var slug = value.Trim().ToLower();


foreach (var c in slug)
{
switch (c)
{
case ' ':
builder.Append("-");
break;
case '&':
builder.Append("and");
break;
default:


if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') && c != '-')
{
builder.Append(c);
}


break;
}
}


return builder.ToString();
}


return string.Empty;
}


public static string Truncate(this string value, int limit)
{
return (value.Length > limit) ? string.Concat(value.Substring(0, Math.Min(value.Length, limit)), "...") : value;
}
}

最后是一些枚举扩展

public static class EnumExtensions
{
public static bool Has<T>(this Enum source, params T[] values)
{
var value = Convert.ToInt32(source, CultureInfo.InvariantCulture);


foreach (var i in values)
{
var mask = Convert.ToInt32(i, CultureInfo.InvariantCulture);


if ((value & mask) == 0)
{
return false;
}
}


return true;
}


public static bool Has<T>(this Enum source, T values)
{
var value = Convert.ToInt32(source, CultureInfo.InvariantCulture);
var mask = Convert.ToInt32(values, CultureInfo.InvariantCulture);


return (value & mask) != 0;
}


public static T Add<T>(this Enum source, T v)
{
var value = Convert.ToInt32(source, CultureInfo.InvariantCulture);
var mask = Convert.ToInt32(v, CultureInfo.InvariantCulture);


return Enum.ToObject(typeof(T), value | mask).As<T>();
}


public static T Remove<T>(this Enum source, T v)
{
var value = Convert.ToInt32(source, CultureInfo.InvariantCulture);
var mask = Convert.ToInt32(v, CultureInfo.InvariantCulture);


return Enum.ToObject(typeof(T), value & ~mask).As<T>();
}


public static T AsEnum<T>(this string value)
{
try
{
return Enum.Parse(typeof(T), value, true).As<T>();
}
catch
{
return default(T);
}
}
}

为了允许更多功能性的组合代码:

    public static Func<T, R> TryCoalesce<T, R>(this Func<T, R> f, R coalesce)
{
return x =>
{
try
{
return f(x);
}
catch
{
return coalesce;
}
};
}
public static TResult TryCoalesce<T, TResult>(this Func<T, TResult> f, T p, TResult coalesce)
{
return f.TryCoalesce(coalesce)(p);
}

然后我可以这样写:

    public static int ParseInt(this string str, int coalesce)
{
return TryCoalesce(int.Parse, str, coalesce);
}

我经常使用的另一个集合是合并 IDictionary 方法:

    public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> d, TKey key, Func<TValue> valueThunk)
{
TValue v = d.Get(key);
if (v == null)
{
v = valueThunk();
d.Add(key, v);
}
return v;
}
public static TValue Get<TKey, TValue>(this IDictionary<TKey, TValue> d, TKey key, TValue coalesce)
{
return Get(d, key, () => coalesce);
}

以及与一般收藏家合作:

    public static IEnumerable<T> AsCollection<T>(this T item)
{
yield return item;
}

然后是树状结构:

    public static LinkedList<T> Up<T>(this T node, Func<T, T> parent)
{
var list = new LinkedList<T>();
node.Up(parent, n => list.AddFirst(n));
return list;
}

所以我可以很容易地遍历和操作一个类,比如:

class Category
{
public string Name { get; set; }
public Category Parent { get; set; }
}

接下来,为了便于复合函数和一种更像 F # 的 C # 编程方式:

public static Func<T, T> Func<T>(this Func<T, T> f)
{
return f;
}
public static Func<T1, R> Compose<T1, T2, R>(this Func<T1, T2> f, Func<T2, R> g)
{
return x => g(f(x));
}

通过定期使用 StringBuilder,您可能会发现需要组合 AppendFormat ()和 AppendLine ()。

public static void AppendFormatLine(this StringBuilder sb, string format, params object[] args)
{
sb.AppendFormat(format, args);
sb.AppendLine();
}

另外,由于我正在将一个应用程序从 VB6转换为 C # ,以下内容对我来说非常有用:

public static string Left(this string s, int length)
{
if (s.Length >= length)
return s.Substring(0, length);
throw new ArgumentException("Length must be less than the length of the string.");
}
public static string Right(this string s, int length)
{
if (s.Length >= length)
return s.Substring(s.Length - length, length);
throw new ArgumentException("Length must be less than the length of the string.");
}

这是在引发事件之前集中空检查的扩展方法。

public static class EventExtension
{
public static void RaiseEvent<T>(this EventHandler<T> handler, object obj, T args) where T : EventArgs
{
EventHandler<T> theHandler = handler;


if (theHandler != null)
{
theHandler(obj, args);
}
}
}

通常,我需要基于 Enum 值显示一个用户友好的值,但是不想使用自定义 Attribute 路由,因为它看起来不太优雅。

使用这种方便的扩展方法:

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());

耶!

这个方法非常简单,但是这是一个我经常做的检查,所以我最终为它制作了一个扩展方法。我最喜欢的扩展方法往往是非常简单、直接的方法,比如这个,或者像 Taylor L 的引发事件的扩展方法。

public static bool IsNullOrEmpty(this ICollection e)
{
return e == null || e.Count == 0;
}

我正在将大量的 Java 转换成 C # 。许多方法仅在大写或其他小的语法差异方面有所不同。所以 Java 代码,比如

myString.toLowerCase();

将不编译,但通过添加一个扩展方法

public static void toLowerCase(this string s)
{
s.ToLower();
}

我可以捕获所有的方法(并且我假设一个好的编译器无论如何都会内联这些方法?)。

这当然使工作更容易和更可靠。 (我感谢@Yuriy-请参阅 在 Java 和 C # 中 StringBuilder 的区别的回答)。

曾经使用过 < em > 实体架构 并且一遍又一遍地使用类似的代码行吗?

_context.EmployeeSet.Include("Department")
.Include("Manager").Include( ... ).Select();

这样做不是更容易吗:

_context.EmployeeSet.IncludeCommonReferenceses().Select();

`

internal static class ObjectContextExtensions
{
internal static ObjectQuery<Employee> IncludeCommonReferenceses(this ObjectQuery<Employee> employeeSet)
{
return employeeSet.Include(GetPropertyName<Employee>(e => e.Department))
.Include(GetPropertyName<Employee>(e => e.Manager)).Include( ... );
}


private static string GetPropertyName<T>(Expression<Func<T, object>> subSelector)
{
return ((MemberExpression)subSelector.Body).Member.Name;
}
}

我建议您将属性名称保存在 const 中,以避免再次使用反射。

我个人收集的字符串实用程序中,我最喜欢的是一个能够从字符串中解析任何具有 TryParse 方法的类型的强类型值的实用程序:

public static class StringUtils
{
/// <summary>
/// This method will parse a value from a string.
/// If the string is null or not the right format to parse a valid value,
/// it will return the default value provided.
/// </summary>
public static T To<t>(this string value, T defaultValue)
where T: struct
{
var type = typeof(T);
if (value != null)
{
var parse = type.GetMethod("TryParse", new Type[] { typeof(string), type.MakeByRefType() });
var parameters = new object[] { value, default(T) };
if((bool)parse.Invoke(null, parameters))
return (T)parameters[1];
}
return defaultValue;
}


/// <summary>
/// This method will parse a value from a string.
/// If the string is null or not the right format to parse a valid value,
/// it will return the default value for the type.
/// </summary>
public static T To<t>(this string value)
where T : struct
{
return value.To<t>(default(T));
}
}

从查询字符串中获取强类型信息非常有用:

var value = Request.QueryString["value"].To<int>();

我讨厌在任何地方都这样做:

DataSet ds = dataLayer.GetSomeData(1, 2, 3);
if(ds != null){
if(ds.Tables.Count > 0){
DataTable dt = ds.Tables[0];
foreach(DataRow dr in dt.Rows){
//Do some processing
}
}
}

相反,我通常使用以下的扩展方法:

public static IEnumerable<DataRow> DataRows(this DataSet current){
if(current != null){
if(current.Tables.Count > 0){
DataTable dt = current.Tables[0];
foreach(DataRow dr in dt.Rows){
yield return dr;
}
}
}
}

所以第一个例子就是:

foreach(DataRow row in ds.DataRows()){
//Do some processing
}

耶,扩展法!

我不喜欢要求属性名作为字符串传递的 INotifyPropertyChanged接口。我希望使用强类型方法在编译时检查是否只引发和处理现有属性的属性更改。我用这个代码来做这件事:

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;
}
}

在实现 INotifyPropertyChanged的类中,我包含了这个 helper 方法:

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());
}
}

这样我终于可以做这种事了:

private string _name;
public string Name
{
get { return _name; }
set { this.NotifySetProperty(ref _name, value, () => this.Name); }
}

它是强类型的,并且我只为实际更改其值的属性引发事件。

将给定 fileinfo 类的任何给定属性与另一个属性进行比较。

    public static bool compare(this FileInfo F1,FileInfo F2,string propertyName)
{
try
{
System.Reflection.PropertyInfo p1 = F1.GetType().GetProperty(propertyName);
System.Reflection.PropertyInfo p2 = F2.GetType().GetProperty(propertyName);


if (p1.GetValue(F1, null) == p2.GetValue(F1, null))
{
return true;
}


}
catch (Exception ex)
{
return false;
}


return false;
}

这是我经常用来从一个流复制到另一个流的东西,特别是复制东西到 MemoryStream。

public static void CopyStream(this Stream destination, Stream source)
{
if (source.CanSeek)
{
source.Position = 0;
}
int Length = 64000;
Byte[] buffer = new Byte[Length];
int bytesRead = source.Read(buffer, 0, Length);
// write the required bytes
while (bytesRead > 0)
{
destination.Write(buffer, 0, bytesRead);
bytesRead = source.Read(buffer, 0, Length);
}
}

实施方法:

MemoryStream result = new MemoryStream();
Stream s = new FileStream(tempDocFile, FileMode.Open);


result.CopyStream(s);


s.Close();

在多线程 WPF 应用程序中(例如,当您使用套接字或计时器时) ,我经常需要调用 GUI 线程来更改 WPF Element 属性。这是丑陋的臃肿代码,尤其是因为您需要为每个方法都这样做。这就是为什么我创建了这个扩展方法:

    /// <summary>
/// Invoke the element's thread using a dispatcher. This is needed for changing WPF element attributes.
/// </summary>
/// <param name="dispatcherObject">The element of which to use the thread.</param>
/// <param name="action">The action to do with the invoked thread.</param>
/// <param name="dispatcherPriority">The priority of this action.</param>
public static void DoInvoked(this System.Windows.Threading.DispatcherObject dispatcherObject, Action action, System.Windows.Threading.DispatcherPriority dispatcherPriority = System.Windows.Threading.DispatcherPriority.Render)
{
if (System.Threading.Thread.CurrentThread == dispatcherObject.Dispatcher.Thread)
{
action();
}
else
{
dispatcherObject.Dispatcher.BeginInvoke(action, dispatcherPriority, null);
}
}

实施方法:

public partial class MainWindow : Window
{
... other code ...
void updateTime(object sender, ElapsedEventArgs e)
{
this.DoInvoked(() => textBoxStatus.Text = "Done.");
}
}

对于约束类型,我可能使用它们最多。

比如:

public class Gallons
{
private int m_gallons;


public Gallons(int count)
{
if(count < 0)
throw new ArgumentException("Cannot have negative gallons");
m_gallons = count;
}


static public Gallons operator + (Gallons left, Gallons right)
{
return new Gallons(left.m_gallons + right.m_gallons);
}
public override string ToString()
{
return m_gallons.ToString();
}
}
public class Feet
{
private int m_feet;


public Feet(int count)
{
if(count < 0)
throw new ArgumentException("Cannot have negative feet");
m_feet = count;
}


static public Feet operator +(Feet left, Feet right)
{
return new Feet(left.m_feet + right.m_feet);
}
public override string ToString()
{
return m_feet.ToString();
}
}


public static class Conversions
{
static public Feet Feet(this int i)
{
return new Feet(i);
}
static public Gallons Gallons(this int i)
{
return new Gallons(i);
}
}


public class Test
{
static public int Main(string[] args)
{
System.Console.WriteLine(2.Feet() + 3.Feet()); // 5
System.Console.WriteLine(3.Gallons() + 4.Gallons()); // 7
System.Console.WriteLine(2.Feet() + 3.Gallons()); // doesn't compile - can't add feet to gallons!
return 0;
}
}

其中一些帖子已经发布了,但我只想说,我看到了一些这样的帖子,投票结果与实际情况不符。IMO,这是真正最有用的扩展方法的列表

someCollection.ForEach(i => i.DoSomething());

这非常非常有用,因为它替换了 foreach 语句中内置的语句,我们都知道它的使用频率。

7.CreateSequence();

这只是创建一个从0到6的序列。可以有其他版本,例如指定起始点和步骤。这是第二个最有用的函数,因为它替换了 for 循环。有人说这是对枚举的复制。Range 函数,这是正确的,但是我喜欢 linq 的一个特点是从左到右的排序,所以您可以这样做

myCollection.Where(i => i.Something == somethingElse).Count().CreateSequence(). do something else

下一个最有用的是 CastTo 和 As。他们再次重复内置的功能,但他们保留了从左到右的顺序。注意,CastTo 与 Cast 的不同之处在于 CastTo 工作在单个对象上。

myObject.CastTo<Person>().DoSomething()
myObject.As<Person>()

然后是 SplitAsEnumable。这与拆分操作相同,但不会一次将所有内存加载到内存中。这对于解析大型文件非常有用。它在字符串或流上工作。

myFileStream.SplitAsEnumerable("\r\n").Select(line => line.SplitAsEnumerable(","))

最后一个是将集合转换为字符串的方法。这对于在屏幕上显示东西或者写入文件非常有用。例如:

myTextBox.Text = "Top 3 scorers are " + myCollection.OrderBy(i => i.Score).Take(3).FlattenToString(i => i.Score.ToString(), ", ");

虽然我没有写这两个-我希望我有。发现在 http://lostechies.com/jimmybogard/2009/10/16/more-missing-linq-operators/

附录

public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element)
{
using (IEnumerator<TSource> e1 = source.GetEnumerator())
while (e1.MoveNext())
yield return e1.Current;


yield return element;
}

假设

public static IEnumerable<TSource> Prepend<TSource>(this IEnumerable<TSource> source, TSource element)
{
yield return element;


using (IEnumerator<TSource> e1 = source.GetEnumerator())
while (e1.MoveNext())
yield return e1.Current;
}