72

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

我今天写的一个例子:

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

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

我迫不及待地想看看其他例子!享受!

4

40 回答 40

19

这是我最近玩的一个:

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

Memento 是一个方便的类:

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);
}
于 2009-06-05T05:33:24.170 回答
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;
}

归根结底,您可以这样称呼它:

someDataTable.ToCSV(); //Plain old CSV
someDataTable.ToCSV("\""); //Double quote qualifier
someDataTable.ToCSV("\"", "\t"); //Tab delimited
于 2009-06-05T04:00:54.090 回答
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;
    }
}

在实现的类中,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); }
}

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

于 2010-02-26T06:39:50.323 回答
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;
    }

它非常简单,但确实有助于清理那些空检查。当您的 UI 需要 X 项列表(如锦标赛系统或玩家位置)并且您想要显示“空座位”时,最好使用它。

用法:

    return jediList.SingleOrDefault( 
                 j => j.LightsaberColor == "Orange", 
               new Jedi() { LightsaberColor = "Orange", Name = "DarthNobody");
于 2009-06-05T04:01:36.727 回答
12

我喜欢使用的两个是我编写的 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);
    }

编辑:
Talljoe 对我仓促构建的 RemoveWhere/RemoveAll 进行了一些重大改进。~3mill 项目每三分之一删除一次,新版本只需要 ~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);
            }
        }
    }
于 2009-06-05T04:09:39.580 回答
11

I have various .Debugify extension methods that are useful for dumping objects to a log file. For example, here's my Dictionary debugify (I have these for List, Datatable, param array, etc.):

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

And here's one for a DbParameterCollection (useful for dumping database calls to the log file):

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

Example result:

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

Note that if you call this after your DB calls, you'll get the output parameters filled in, too. I call this on a line that includes the SP name so I can copy/paste the call into SSMS for debugging.


These make my log files pretty and easy to generate without interrupting my code.

于 2009-06-05T04:34:50.937 回答
11

这是我一起破解的一个,所以请随意挑选其中的漏洞。它接受一个(有序的)整数列表并返回一个连续范围的字符串列表。例如:

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() 将其转换为 IEnumerable<int>,在本例中为 [3,4,6,7,8],它变为“3-4,6-8”。它为用户提供了讲座发生的学术周范围的简要描述。

于 2009-06-05T04:06:32.733 回答
9

一对将 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! )

于 2009-06-05T04:25:57.990 回答
7

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

于 2009-06-05T08:44:14.310 回答
5

虽然非常简单,但我发现这个特别有用,因为我从一个完整的结果集中获得了一个页面 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;
    }
}
于 2009-06-06T03:28:11.300 回答
5

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

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);
        }
    }
}
于 2009-08-23T00:40:56.120 回答
5

很多时候,我需要根据 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());

耶!

于 2009-09-13T06:34:23.590 回答
5

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

枚举的扩展方法有话要说,因为它们不可能有方法。如果将它们定义在与 Enum 相同的命名空间和相同的程序集中,它们将透明地工作。

于 2009-06-05T08:29:37.177 回答
3

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

public static bool IsNullOrEmpty(this ICollection e)
{
    return e == null || e.Count == 0;
}
于 2009-09-13T06:42:52.840 回答
2

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

myString.toLowerCase();

不会编译,而是通过添加扩展方法

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

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

这无疑使工作变得更容易、更可靠。(我感谢@Yuriy - 请参阅答案:Java 和 C# 中的 StringBuilder 之间的差异)的建议。

于 2009-10-11T14:22:37.910 回答
2

我经常使用的另一组是用于合并 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));
}
于 2009-06-11T16:36:59.940 回答
2

要允许更多功能的组合器代码:

    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);
    }
于 2009-06-10T23:08:54.590 回答
1

我个人收藏的字符串实用程序中我最喜欢的是一个可以从字符串中解析强类型值的任何类型的具有 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>();
于 2009-11-04T15:22:51.323 回答
1

This one creates array with single element added at the very beginning:

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

And this one helps me when I need to convert some expression to it's square:

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

(x - x0).Sq() + (y - y0).Sq() + (z - z0).Sq()
于 2009-06-05T05:00:06.490 回答
1

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:

Usage:

        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!!

于 2009-06-05T05:05:29.040 回答
1

经常使用 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.");
}
于 2009-07-16T13:48:07.100 回答
1

我在这里提到了几个我使用的:

于 2009-06-05T10:49:25.663 回答
1

很酷,也很喜欢扩展!

这里有几个。

这将获得一个月的最后一个日期:

<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
于 2009-06-05T06:14:01.327 回答
1

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.

于 2009-06-05T08:02:37.997 回答
1

我喜欢这个。它是 String.Split 方法的一种变体,当拆分字符打算在实际字符串中时,它允许使用转义字符来抑制拆分。

于 2009-06-05T03:55:04.623 回答
1

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;
}
于 2009-06-05T04:21:14.603 回答
1

我主要使用的几个扩展。第一组是对象扩展,实际上只用于转换。

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);
        }
    }
}
于 2009-06-07T09:00:45.513 回答
1

我讨厌到处都这样做:

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
}

耶,扩展方法!

于 2009-11-26T01:59:30.847 回答
0

将给定文件信息类的任何给定属性与另一个属性进行比较。

    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;
    }
于 2010-02-26T07:00:41.153 回答
0

这会改变一个序列,以便您首先获得给定的项目。例如,我使用它来获取星期几并将其转换,以便序列中的第一天是当前文化一周的第一天。

    /// <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);
    }
于 2009-06-05T07:49:38.820 回答
0

我自动执行以下操作来解决 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 = "" } 
);
于 2009-06-05T10:50:31.933 回答
0
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);
        }
    }
}
于 2009-06-07T02:16:19.473 回答
0

我可能最常将它们用于受约束的类型。

就像是:

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;
    }
}
于 2010-03-06T00:49:14.300 回答
0

这是我最近在工作中写的一篇博客:

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

它基本上是 IEnumerable.ToHtmlTable();

于 2009-06-05T04:12:13.980 回答
0

String.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
于 2009-06-05T05:24:42.293 回答
0

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

    /// <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.");
    }
}
于 2010-03-06T00:29:15.943 回答
0

这是我经常用来从一个流复制到另一个流的东西,尤其是用于将内容复制到 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();
于 2010-02-26T14:48:17.390 回答
0

曾经使用过实体框架并一遍又一遍地使用与此类似的代码行吗?

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

我建议您将属性名称保存在 consts 中,以避免在agasin 上使用反射。

于 2009-10-19T07:20:56.967 回答
0

其中一些已经发布,但我只想说我已经看到了许多这样的线程,并且投票与有用的现实不符。IMO,这是真正最有用的扩展方法列表

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

这非常有用,因为它取代了内置的 foreach 语句,而且我们都知道它被使用的频率。

7.CreateSequence();

这只是创建了一个从 0 到 6 的序列。可以有其他版本,例如指定起点和步骤。这是第二个最有用的函数,因为它取代了 for 循环。有人说这重复了 Enumerable.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>()

然后是 SplitAsEnumerable。这与 split 的工作方式相同,但不会一次将所有内容加载到内存中。这对于解析大文件非常有用。它适用于字符串或流。

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(), ", ");
于 2011-08-30T23:48:05.127 回答
0

虽然我没有写这两个 - 我希望我有。在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;
}
于 2011-09-12T14:52:54.873 回答