当前位置:  开发笔记 > 编程语言 > 正文

你见过的扩展方法最好或最有趣的用途是什么?

如何解决《你见过的扩展方法最好或最有趣的用途是什么?》经验,为你挑选了13个好方法。

我开始非常喜欢延伸方法......我想知道是否有人偶然发现了一个真正引起他们注意的人,或者只是发现了聪明.

我今天写的一个例子:

由于其他用户的评论而编辑:

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

我迫不及待想看其他例子!请享用!



1> Josh..:

完整的解决方案太大了,不能放在这里,但我写了一系列扩展方法,可以让你轻松地将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



2> Talljoe..:

这是最近一直在玩我的游戏:

public static IDisposable Tag(this HtmlHelper html, string tagName)
{
    if (html == null)
        throw new ArgumentNullException("html");

    Action a = tag => html.Write(String.Format(tag, tagName));
    a("<{0}>");
    return new Memento(() => a(""));
}

使用如下:

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


我喜欢这个想法,但我不确定为什么Memento会从它的Dispose方法中抛出异常.根据MSDN:"为了确保资源总是被适当地清理,Dispose方法应该可以多次调用而不会抛出异常." http://bit.ly/NV3AH

3> Enigmativity..:

我不是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); }
}

它是强类型的,我只为实际改变其值的属性引发事件.



4> John Farrell..:

嗯,这不是很聪明,但我修改了---- 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");


我个人认为它不比.SingleOrDefault()??新Foo()干净得多
请注意,对于非可空类型,这将无法正常工作.当没有找到任何项时,内置的SingleOrDefault扩展返回默认值(T),对于引用类型或可空值类型,这仅为null.

5> geofftnz..:

这是我一起砍的一个,所以随意挑选它.它采用(有序)整数列表并返回连续范围的字符串列表.例如:

1,2,3,7,10,11,12  -->  "1-3","7","10-12"

该函数(在静态类中):

public static IEnumerable IntRanges(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 ,在本例中为[3,4,6,7,8],变为"3-4,6-8".它为用户提供了讲座发生的学术周范围的简洁描述.



6> rmoore..:

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

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


RemoveWhere在删除项目时由于所有内存移位而有些低效(除非IList是LinkedList).我在这里创建了一个修改后的版本:http://pastebin.com/f20e73b4e差异:1)重命名为"RemoveAll"以匹配List 的版本.2)Call List 的版本(如果适用)(甚至比我的版本3更高效)使用两个索引来遍历列表并对值进行就地覆盖.4)当有人通过数组时处理大小写(我想我实际上更喜欢抛出异常,但我想在修改数组之前先做这个 - 为读者练习).

7> Michael Hare..:

我有各种.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) {
    List ParameterValuesList = 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中进行调试.


这些使我的日志文件非常容易生成而不会中断我的代码.



8> geofftnz..:

一对扩展方法,用于将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! )


嗯,需要有一个导入函数来取你的例子并直接将它贴在每日wtf上.(虽然我最近有一个客户端,是创造和他们的分贝使用自定义的GUID咨询.创建一个GUID,剪除并添加时间戳.严重WTF).

9> Thomas Leves..:

我编写了一系列扩展方法,以便更容易地操作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中),请参见此处.



10> Thorarin..:

我在这里看到的扩展方法的大多数示例都违背了最佳实践.扩展方法功能强大,但应谨慎使用.根据我的经验,具有旧式语法的静态助手/实用程序类通常对于大多数这些是优选的.

对于Enums的扩展方法,有一些话要说,因为它们不可能有方法.如果您在与Enum相同的命名空间中定义它们并在同一个程序集中,它们将以透明方式工作.



11> jrista..:

虽然非常简单,但我发现这个特别有用,因为我从一个完整的结果集中获得了一个项目的100亿次页面:

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

        return query;
    }
}



12> Taylor Leese..:

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

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



13> Mark Carpent..:

通常,我需要根据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());

好极了!

推荐阅读
和谐啄木鸟
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有