Mar*_*ter 71 .net c# extension-methods syntactic-sugar
我开始非常喜欢延伸方法......我想知道是否有人偶然发现了一个真正引起他们注意的人,或者只是发现了聪明.
我今天写的一个例子:
由于其他用户的评论而编辑:
public static IEnumerable<int> To(this int fromNumber, int toNumber) {
while (fromNumber < toNumber) {
yield return fromNumber;
fromNumber++;
}
}
Run Code Online (Sandbox Code Playgroud)
这允许将for循环写为foreach循环:
foreach (int x in 0.To(16)) {
Console.WriteLine(Math.Pow(2, x).ToString());
}
Run Code Online (Sandbox Code Playgroud)
我迫不及待想看其他例子!请享用!
Jos*_*osh 18
完整的解决方案太大了,不能放在这里,但我写了一系列扩展方法,可以让你轻松地将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;
}
Run Code Online (Sandbox Code Playgroud)
在一天结束时,您可以这样称呼它:
someDataTable.ToCSV(); //Plain old CSV
someDataTable.ToCSV("\""); //Double quote qualifier
someDataTable.ToCSV("\"", "\t"); //Tab delimited
Run Code Online (Sandbox Code Playgroud)
Tal*_*joe 18
这是最近一直在玩我的游戏:
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}>"));
}
Run Code Online (Sandbox Code Playgroud)
使用如下:
using (Html.Tag("ul"))
{
this.Model.ForEach(item => using(Html.Tag("li")) Html.Write(item));
using(Html.Tag("li")) Html.Write("new");
}
Run Code Online (Sandbox Code Playgroud)
纪念品是一个方便的课程:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
并完成依赖:
public static void Write(this HtmlHelper html, string content)
{
if (html == null)
throw new ArgumentNullException("html");
html.ViewContext.HttpContext.Response.Write(content);
}
Run Code Online (Sandbox Code Playgroud)
Eni*_*ity 13
我不是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;
}
}
Run Code Online (Sandbox Code Playgroud)
在实现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());
}
}
Run Code Online (Sandbox Code Playgroud)
所以最后我可以做这样的事情:
private string _name;
public string Name
{
get { return _name; }
set { this.NotifySetProperty(ref _name, value, () => this.Name); }
}
Run Code Online (Sandbox Code Playgroud)
它是强类型的,我只为实际改变其值的属性引发事件.
Joh*_*ell 12
嗯,这不是很聪明,但我修改了---- 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;
}
Run Code Online (Sandbox Code Playgroud)
它令人难以置信的简单,但真的有助于清理那些空检查.当您的UI期望X项目列表(如锦标赛系统或游戏玩家插槽)并且您希望显示"空座位"时,最佳使用.
用法:
return jediList.SingleOrDefault(
j => j.LightsaberColor == "Orange",
new Jedi() { LightsaberColor = "Orange", Name = "DarthNobody");
Run Code Online (Sandbox Code Playgroud)
geo*_*tnz 11
这是我一起砍的一个,所以随意挑选它.它采用(有序)整数列表并返回连续范围的字符串列表.例如:
1,2,3,7,10,11,12 --> "1-3","7","10-12"
Run Code Online (Sandbox Code Playgroud)
该函数(在静态类中):
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();
}
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
this.WeekDescription = string.Join(",", from.WeekPattern.WeekPatternToInts().IntRanges().ToArray());
Run Code Online (Sandbox Code Playgroud)
此代码用于转换DailyWTF值得的时间表应用程序中的数据.WeekPattern是存储在字符串"0011011100 ..."中的位掩码.WeekPatternToInts()将其转换为IEnumerable <int>,在本例中为[3,4,6,7,8],变为"3-4,6-8".它为用户提供了讲座发生的学术周范围的简洁描述.
rmo*_*ore 11
我喜欢使用的两个是我编写的InsertWhere <T>和RemoveWhere <T>扩展方法.在WPF和Silverlight中使用ObservableCollections我经常需要修改有序列表而不重新创建它们.这些方法允许我根据提供的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);
}
Run Code Online (Sandbox Code Playgroud)
编辑:
Talljoe对RemoveWhere/RemoveAll进行了一些重大改进,我已经匆忙构建了.每三个项目删除大约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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
Mic*_*ren 10
我有各种.Debugify扩展方法,可用于将对象转储到日志文件.例如,这是我的字典调试(我有这些用于List,Datatable,param数组等):
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;
}
Run Code Online (Sandbox Code Playgroud)
这里有一个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());
}
Run Code Online (Sandbox Code Playgroud)
示例结果:
Log.DebugFormat("EXEC {0} {1}", procName, params.Debugify);
// EXEC spProcedure @intID=5, @nvName='Michael Haren', @intRefID=11 OUTPUT
Run Code Online (Sandbox Code Playgroud)
请注意,如果在数据库调用后调用此方法,您也将获得输出参数.我在一个包含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;
}
Run Code Online (Sandbox Code Playgroud)
(有些天才决定在数据库中存储数字的最佳方法是将它们编码为字符串.小数点占用太多空间.十六进制更好,但不使用字符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;
}
Run Code Online (Sandbox Code Playgroud)
将参数添加到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;
}
Run Code Online (Sandbox Code Playgroud)
按名称而不是索引访问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);
}
...
Run Code Online (Sandbox Code Playgroud)
另一个(不相关的)扩展方法允许我在WinForms表单和控件上执行DragMove操作(如在WPF中),请参见此处.
我在这里看到的扩展方法的大多数示例都违背了最佳实践.扩展方法功能强大,但应谨慎使用.根据我的经验,具有旧式语法的静态助手/实用程序类通常对于大多数这些是优选的.
对于Enums的扩展方法,有一些话要说,因为它们不可能有方法.如果您在与Enum相同的命名空间中定义它们并在同一个程序集中,它们将以透明方式工作.
虽然非常简单,但我发现这个特别有用,因为我从一个完整的结果集中获得了一个项目的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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是一种在引发事件之前集中空检查的扩展方法.
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
通常,我需要根据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!!";
}
Run Code Online (Sandbox Code Playgroud)
我可以做这个:
Console.WriteLine(MyEnum.First.EnumValue());
Run Code Online (Sandbox Code Playgroud)
好极了!
| 归档时间: |
|
| 查看次数: |
10269 次 |
| 最近记录: |