什么是你见过的扩展方法的最好或最有趣的用法?
我开始真的很喜欢延伸方法……我想知道是否有人偶然发现了一个真的让自己的头脑发飙的东西,或者只是发现了聪明的东西。
我今天写的一个例子是:
由于其他用户的评论而编辑:
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()); }
我等不及看到其他的例子! 请享用!
完整的解决scheme太大,不能放在这里,但我写了一系列的扩展方法,可以让你轻松地将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"); 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); }
我不是INotifyPropertyChanged
接口的粉丝,要求将属性名称作为string传递。 我想要一个强types的方式来检查在编译时,我只提高和处理属性更改存在的属性。 我使用这个代码来做到这一点:
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
类中,我包含这个辅助方法:
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); } }
它是强types的,我只为事实上改变其价值的属性引发事件。
那么这不是很聪明,但我已经修改了—- 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");
这里有一个我一起入侵,所以随便挑一个洞。 它需要一个(有序的)整数列表并返回一个连续范围的string列表。 例如:
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是一个存储在string“0011011100 …”中的位掩码。 WeekPatternToInts()将其转换为IEnumerable <int>,在本例中为[3,4,6,7,8],变成“3-4,6-8”。 它为用户提供了讲座发生的学术周范围的简要描述。
我喜欢使用的两个是我写的InsertWhere <T
>和RemoveWhere <T
>扩展方法。 在WPF和Silverlight中使用ObservableCollections我经常需要修改有序列表而不重新创build它们。 这些方法允许我根据提供的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进行了一些重大的改进,我已经草草build造了。 有3毫米的物品每三分之一移除一个新版本只需要约50毫秒(如果它可以调用List.RemoveAll!则不到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); } } }
我有各种各样的.Debugify
扩展方法是有用的转储对象到日志文件。 例如,这里是我的字典debugging(我有这些列表,数据表,参数数组等):
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; }
这里有一个DbParameterCollection(用于将数据库调用转储到日志文件中):
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中进行debugging。
这些使我的日志文件非常容易,而不会中断我的代码。
一对将base-36string(!)转换为整数的扩展方法:
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; }
(有些天才决定把数字存储在数据库中的最好方法是把它们编码为string,小数点需要太多的空间,hex比较好,但是不使用字符GZ,所以显然你把base-16扩展到base-36! )
我编写了一系列扩展方法,以便更容易地操作ADO.NET对象和方法:
用一条指令从DbConnection创build一个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中), 请参阅此处 。
我在这里看到的扩展方法的大多数示例都违背了最佳实践。 扩展方法function强大,但应谨慎使用。 根据我的经验,对于其中的大多数来说,具有旧式语法的静态助手/实用程序类通常是可取的。
对于Enums的扩展方法有些话要说,因为它们不可能有方法。 如果您将它们定义在与Enum相同的名称空间中,并且位于同一个程序集中,则它们将以透明方式工作。
虽然非常简单,但是我发现这个特别有用,因为我从一个完整的结果集中获得了一百亿次项目的页面:
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; } }
很多时候,我需要基于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());
好极了!
这是在提升事件之前集中空检查的扩展方法。
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); } } }
这个是非常简单的,但这是一个检查,我做了很多,所以我最终做了一个扩展方法。 我最喜欢的扩展方法往往是这样的简单,直接的方法,或者像泰勒的延伸方法来提高事件。
public static bool IsNullOrEmpty(this ICollection e) { return e == null || e.Count == 0; }
为了允许更多的function组合码:
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; } }
接下来,为了方便C#编程中的函数组合和更多的F#方法:
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)); }
我正在将大量的Java转换为C#。 许多方法仅在大写或其他小的语法差异方面有所不同。 所以Java代码如
myString.toLowerCase();
不会编译,而是通过添加扩展方法
public static void toLowerCase(this string s) { s.ToLower(); }
我可以捕捉所有的方法(我假设一个好的编译器将内联这个呢?)。
当然,这项工作更容易,更可靠。 (我感谢@Yuriy – 请参阅Java中的StringBuilder和C#之间的差异 )的build议。
我喜欢这个 。 它是String.Split方法的一个变体,它允许使用转义字符来抑制分割字符在实际string中的分割。
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; }
这个创build的数组在开头添加了单个元素:
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");
当我需要将一些expression式转换为方形时,这个帮助我:
public static double Sq(this double arg) { return arg * arg; } (x - x0).Sq() + (y - y0).Sq() + (z - z0).Sq()
Here's another one I wrote:
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); } }
The motivation behind this one was simple. It always bugged me how the built in Substring method took startindex and length as it's parameters. It's ALWAYS much more helpful to do startindex and endindex. So, I rolled my own:
用法:
string s = "This is a tester for my cool extension method!!"; s = s.Subsetstring("tester", "cool",true);
The reason I had to use Subsetstring was because Substring's overload already takes two ints. If anyone has a better name, please, let me know!!
cool, also loving Extensions!
here's a few.
This one will get the last Date of a Month:
<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
these two make reflection a bit easier:
<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
The extension methods I use the most would have to be the ones in the System.Linq.Enumerable
class.
And a good and useful extension to that list you can find in MoreLinq .
There are a couple that I've mentioned here that I use:
-
Easier checking on flags enums
if( enumVar.IsSet( MyEnum.PossibleFlag ) ) //..then
-
Inline checking of nulls
myObject.IfNotNull( x => x.Property );
few extensions I use mostly. first set is object extensions, really only for converting.
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; } }
string extensions
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; } }
and last is some enum extensions
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); } } }
With regular use of StringBuilder, you may see the need to combine AppendFormat() and AppendLine().
public static void AppendFormatLine(this StringBuilder sb, string format, params object[] args) { sb.AppendFormat(format, args); sb.AppendLine(); }
Also, since I'm converting an application from VB6 to C#, the following are very useful to me:
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."); }
My favourite from my own personal collection of string utils is one that will parse a strongly typed value from a string for any type that has a TryParse method:
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)); } }
It's great for getting strongly typed information from query strings:
var value = Request.QueryString["value"].To<int>();
I hate having to do this everywhere:
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 } } }
Instead I usually use the following Extension Method:
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; } } } }
So the first example then becomes:
foreach(DataRow row in ds.DataRows()){ //Do some processing }
Yay, Extension Methods!
String.format should not have been static. So I use an extension method called 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
When I want to read or write a number to a byte stream without constructing a binary writer (technically you aren't supposed to modify the raw stream after you've wrapped it with a writer):
<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
This one shifts a sequence so that you get the given item first. I used it for example to take the day of weeks and shift it so that the first day in the sequence is the first day of the week for the current culture.
/// <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); }