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

73

我开始真的喜欢扩展方法... 我正在想是否有人在这里发现了真正令人惊叹或聪明的例子。

我今天写的一个例子:

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

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

我迫不及待地想看到其他的例子!祝你愉快!


19
你的方法大多数是Enumerable.Range(http://msdn.microsoft.com/en-us/library/system.linq.enumerable.range.aspx)的重新实现。不同之处在于Range接受起始值和计数,而你的方法接受起始值和结束值。同时,你的方法也与常规的限定惯例(<)不同,它包括了上限(<=)。最后,该方法可以反向迭代,但在实践中很少需要这样做。 - Matthew Flaschen
8
违反正常的界限惯例?胡说八道。在口语和概念中,“0到16”始终是包含在内的。在for循环中,通常使用max + 1作为条件中的数字,因为在一个5项列表中,索引从0到4,使用“< 5”比“<= 4”更有意义。 - Sander
5
阅读此处:https://dev59.com/G3VC5IYBdhLWcg3whBaj - tuinstoel
6
我认为 for(int x=0; x<=16; ++x) 对有经验的程序员来说更易读。但是,闭区间往往较少使用。 - Tom Hawtin - tackline
3
像这样的问题让我更想写更多的C#代码... - Daniel Huckstep
显示剩余2条评论
40个回答

5

经常需要根据枚举值显示用户友好的值,但是不想使用自定义属性路线,因为这似乎不太优雅。

有了这个方便的扩展方法:

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

太棒了!


5

虽然非常简单,但我发现这个解决方案在每个项目中都可以生成满足完整结果集的页面。

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

为什么不把它写成一行呢? - nawfal
@nawfal:在一行上写什么?这是一个方便的扩展,你必须计算skipCount……那么……? - jrista
我认为这样写更好:return query.Skip((pageNumber - 1) * pageSize).Take(pageSize)。只是个人口味问题。 - nawfal
@nawfal:你可以自由地重新格式化。就我个人而言,我喜欢将语句分开,主要是为了便于调试(即我可以在 int skipCount 行上设置断点并在手头评估数字,而如果将所有内容合并成一行,则调试能力会大大降低。每行的查询变量也是如此...当我逐步执行代码时,我可以独立地评估结果)。 - jrista
好的,我同意使用int skipcount行。但是Linq应该写在一行中,因为将其分成两行并没有帮助你 :) 这只是我的想法。 - nawfal
显示剩余2条评论

5

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

有些情况下,枚举类型可以使用扩展方法,因为它们无法具有方法。如果您将其定义在与枚举相同的命名空间和程序集中,则它们可以透明地工作。


4

这个很简单,但我经常检查它,所以最终做了一个扩展方法。我最喜欢的扩展方法通常是非常简单、直接的,比如像Taylor L的用于触发事件的扩展方法。

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

1
你可以使用 IEnumerable 代替 ICollection - WiiMaxx

2

我正在将很多Java代码转换为C#。其中许多方法只有大小写或其他小语法差异。因此,Java代码如下:

myString.toLowerCase();

如果不添加扩展方法,代码将无法编译

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

我可以捕获所有方法(我认为一个好的编译器会内联这个方法?)。

这无疑使工作变得更加容易和可靠。 (我感谢@Yuriy-请参见:Java和C#中StringBuilder的区别),提供了这个建议。


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

问题在于,由于没有限定条件的“catch”,这将使你的代码在面对致命异常(AccessViolation、ExecutionEngineException、OutOufMemoryException等)时变得不可靠。 - Christian.K
1
当代码耗尽内存时,大多数代码往往不可靠... - user24359

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

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

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
}

太棒了,扩展方法!


你的数据集中有多少次出现了>1个表格?只需使用foreach (DataRow dr in ds.Tables [0].Rows)。 - tsilb

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接