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

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个回答

1

对int类型做扩展方法,将一个位掩码解码为星期几的枚举(在此情况下,第一天为星期一):

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

为什么在这里使用yield?我很好奇这是如何工作的...如果你的dayMask是星期一和星期五,即0010001,使用yield return与return有什么影响? - Jeff Meatball Yang
啊,我明白了。它基本上在你迭代位掩码时从上次离开的地方开始运行。明白了。 - Jeff Meatball Yang

1

这个代码创建了一个数组,并在开头添加了一个元素:

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

这个函数可以帮助我将某些表达式转换为它们的平方:

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

(x - x0).Sq() + (y - y0).Sq() + (z - z0).Sq()

3
天啊!在数组前插入真的是一个非常糟糕的想法,从概念上讲。非常确定您想要/需要这样做... 将其制作成扩展方法会很容易陷入通过在最初为空(或小)的数组中前置大量值来填充大型数组的陷阱,在此过程中每个中间数组都会生成一个新的临时副本!就我个人而言,我不会这样做。(如果已经有数组Append,同样适用。) - peSHIr
1
我确定我需要这个。这是由广泛使用数组并且它们的修改很少发生的代码使用的。 - okutane
我同意这是一件你通常应该小心使用的事情。也许你可以包含一个重载,接受多个要预置的项目,以避免在循环中调用你的方法。 - Drew Noakes
1
如果您必须使用数组,此类操作工具会很有帮助。如果您有同事可能会错误地使用它,您可以为其指定一个更能反映其缺点的名称,例如'PrependSlow'或'PrependAndCopy'。我喜欢这个+1。 - Michael Haren

1

这是我写的另一个:

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

这个东西的动机很简单。内置的子字符串方法总是让我感到困扰,因为它将起始索引和长度作为参数。始终使用起始索引和结束索引要更有帮助。因此,我自己开发了一个:
用法:
        string s = "This is a tester for my cool extension method!!";
        s = s.Subsetstring("tester", "cool",true);

我不得不使用Subsetstring的原因是Substring的重载已经接受了两个整数。如果有更好的名称,请告诉我!

我真不明白为什么我自己没做过这件事。+1 - Torbjørn
也许你可以使用 SubstringBetween 或者仅仅是 Between 这样的名称。 - Drew Noakes
4
如果你想编辑一篇文章的话,那没问题,但是如果你改变代码,因为你认为这样更好,我觉得有点过了。虽然我不会撤销,但我认为这样做有点过头了。 - BFree
1
这就是维基的魅力! - Cheeso

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

+1 太棒了!我在处理字符串时不熟悉“slug”这个术语。能否分享一下您如何使用这个特定的方法?谢谢! - Mark Carpenter
2
基本上它会将“Cars&Trucks”转换为“cars-and-trucks”。 - Mike Geise
为什么不直接调用 HttpUtility.UrlEncode() 呢? - tsilb
@tslib,这一切都与SEO有关,这就是为什么我们首先要将字符串转换为slug的原因。 - Mike Geise

1

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


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

1
你可以将其拆分为两个单独的答案。这是 Stack Overflow 的方式! - Drew Noakes
1
那个 GetLastMonthDay 的实现太可怕了!只需要一行代码:return source.AddMonths(1).AddDays(-(source.Day + 1)); - Task

1

我使用最多的扩展方法应该是在 System.Linq.Enumerable 类中的那些。

而且,你可以在 MoreLinq 中找到一个很好且有用的扩展方法列表。


1

0

其中一些已经发布过了,但我想说我看到了很多这样的帖子,投票结果从来不符合实际的有用性。在我看来,以下是真正最有用的扩展方法列表。

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

那么实现在哪里呢? - nawfal

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

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