你最喜欢的C#扩展方法是什么?(codeplex.com/extensionoverflow)

478

让我们列出一份答案,其中您可以发布您卓越且最喜欢的扩展方法

要求是必须发布完整代码,并提供示例以及如何使用它的解释。

基于对此主题的高度关注,我在Codeplex上建立了一个名为extensionoverflow的开源项目。

请标记您的答案以接受将代码放入Codeplex项目中。

请发布完整的源代码而不是链接。

Codeplex新闻:

2010年08月24日 Codeplex页面现在在这里:http://extensionoverflow.codeplex.com/

2008年11月11日 XmlSerialize / XmlDeserialize已经实现并通过单元测试

2008年11月11日 还有更多开发人员的空间。;-) 现在加入吧!

2008年11月11日 第三个贡献者加入ExtensionOverflow,欢迎BKristensen

2008年11月11日,FormatWith现已实现通过单元测试

2008年11月09日,第二位贡献者加入了ExtensionOverflow。欢迎chakrit

2008年11月09日,我们需要更多的开发人员。;-)

2008年11月09日,ThrowIfArgumentIsNull现已实现通过单元测试在Codeplex上。


现在第一段代码已经提交到 Codeplex 网站。 - bovium
Erik,不幸的是,现在所有的事情都已经在CodePlex上开始了。请无论如何加入。 - bovium
3
看起来还不错。我对静态类的命名有一点评论。将它们命名为<type>Extensions并不是很具指示性。例如,StringExtensions 既包含格式化内容又包含 XML 内容。我认为更好的做法是根据你扩展的类型来命名类。例如,在 UnixDateTimeConversions 中命名。你可以合理地猜测它包含将时间转换为 Unix 时间及从 Unix 时间转换的方法。这只是一个想法! - ecoffey
请查看以下网址了解有关C#扩展方法的更多信息:http://planetofcoders.com/c-extension-methods/ - Gaurav Agrawal
150个回答

4
我觉得以下扩展方法非常有用:
```

我发现以下扩展方法非常有用:

```
public static T GetService<T>(this IServiceProvider provider)
{
    return (T)provider.GetService(typeof(T));
}

使用IServiceProvider接口会更加方便。对比如下:

IProvideValueTarget target = (IProvideValueTarget)serviceProvider(typeof(IProvideValueTarget));

并且

var target = serviceProvider.GetService<IProvideValueTarget>();

4
我今天刚发布了一篇博客它是一个强类型的反应式封装器,围绕着INotifyPropertyChanged属性。 GetPropertyValues返回一个IObservable<T>值,随着值的改变而改变,从当前值开始。如果忽略当前值,您可以直接在结果上调用Skip(1)
使用方法如下:
IObservable<int> values = viewModel.GetPropertyValues(x => x.IntProperty);

实现:

public static class NotifyPropertyChangeReactiveExtensions
{
    // Returns the values of property (an Expression) as they change, 
    // starting with the current value
    public static IObservable<TValue> GetPropertyValues<TSource, TValue>(
        this TSource source, Expression<Func<TSource, TValue>> property)
        where TSource : INotifyPropertyChanged
    {
        MemberExpression memberExpression = property.Body as MemberExpression;

        if (memberExpression == null)
        {
            throw new ArgumentException(
                "property must directly access a property of the source");
        }

        string propertyName = memberExpression.Member.Name;

        Func<TSource, TValue> accessor = property.Compile();

        return source.GetPropertyChangedEvents()
            .Where(x => x.EventArgs.PropertyName == propertyName)
            .Select(x => accessor(source))
            .StartWith(accessor(source));
    }

    // This is a wrapper around FromEvent(PropertyChanged)
    public static IObservable<IEvent<PropertyChangedEventArgs>>
        GetPropertyChangedEvents(this INotifyPropertyChanged source)
    {
        return Observable.FromEvent<PropertyChangedEventHandler, 
            PropertyChangedEventArgs>(
            h => new PropertyChangedEventHandler(h),
            h => source.PropertyChanged += h,
            h => source.PropertyChanged -= h);
    }
}

4

这些扩展方法可以异步地调用事件。它们的灵感来自于这个StackOverflow答案

/// <summary>
/// Invoke an event asynchronously. Each subscriber to the event will be invoked on a separate thread.
/// </summary>
/// <param name="someEvent">The event to be invoked asynchronously.</param>
/// <param name="sender">The sender of the event.</param>
/// <param name="args">The args of the event.</param>
/// <typeparam name="TEventArgs">The type of <see cref="EventArgs"/> to be used with the event.</typeparam>
public static void InvokeAsync<TEventArgs>(this EventHandler<TEventArgs> someEvent, object sender, TEventArgs args)
    where TEventArgs : EventArgs
{
    if (someEvent == null)
    {
        return;
    }

    var eventListeners = someEvent.GetInvocationList();

    AsyncCallback endAsyncCallback = delegate(IAsyncResult iar)
    {
        var ar = iar as AsyncResult;
        if (ar == null)
        {
            return;
        }

        var invokedMethod = ar.AsyncDelegate as EventHandler<TEventArgs>;
        if (invokedMethod != null)
        {
            invokedMethod.EndInvoke(iar);
        }
    };

    foreach (EventHandler<TEventArgs> methodToInvoke in eventListeners)
    {
        methodToInvoke.BeginInvoke(sender, args, endAsyncCallback, null);
    }
}

/// <summary>
/// Invoke an event asynchronously. Each subscriber to the event will be invoked on a separate thread.
/// </summary>
/// <param name="someEvent">The event to be invoked asynchronously.</param>
/// <param name="sender">The sender of the event.</param>
/// <param name="args">The args of the event.</param>
public static void InvokeAsync(this EventHandler someEvent, object sender, EventArgs args)
{
    if (someEvent == null)
    {
        return;
    }

    var eventListeners = someEvent.GetInvocationList();

    AsyncCallback endAsyncCallback = delegate(IAsyncResult iar)
    {
        var ar = iar as AsyncResult;
        if (ar == null)
        {
            return;
        }

        var invokedMethod = ar.AsyncDelegate as EventHandler;
        if (invokedMethod != null)
        {
            invokedMethod.EndInvoke(iar);
        }
    };

    foreach (EventHandler methodToInvoke in eventListeners)
    {
        methodToInvoke.BeginInvoke(sender, args, endAsyncCallback, null);
    }
}

使用方法:

public class Foo
{
    public event EventHandler<EventArgs> Bar;

    public void OnBar()
    {
        Bar.InvokeAsync(this, EventArgs.Empty);
    }
}

注意到一个额外的好处,您在调用事件之前不必检查事件是否为空。例如:
EventHandler<EventArgs> handler = Bar;
if (handler != null)
{
    // Invoke the event
}

测试方法:

void Main()
{
    EventHandler<EventArgs> handler1 =
    delegate(object sender, EventArgs args)
    {
        // Simulate performing work in handler1
        Thread.Sleep(100);
        Console.WriteLine("Handled 1");
    };

    EventHandler<EventArgs> handler2 =
    delegate(object sender, EventArgs args)
    {
        // Simulate performing work in handler2
        Thread.Sleep(50);
        Console.WriteLine("Handled 2");
    };

    EventHandler<EventArgs> handler3 =
    delegate(object sender, EventArgs args)
    {
        // Simulate performing work in handler3
        Thread.Sleep(25);
        Console.WriteLine("Handled 3");
    };

    var foo = new Foo();
    foo.Bar += handler1;
    foo.Bar += handler2;
    foo.Bar += handler3;
    foo.OnBar();

    Console.WriteLine("Start executing important stuff");

    // Simulate performing some important stuff here, where we don't want to
    // wait around for the event handlers to finish executing
    Thread.Sleep(1000);

    Console.WriteLine("Finished executing important stuff");
}

调用事件通常会产生以下输出:

开始执行重要的事情
已处理3个
已处理2个
已处理1个
完成执行重要的事情

如果同步调用事件,它将始终产生此输出,并延迟执行“重要”任务:

已处理1个
已处理2个
已处理3个
开始执行重要的事情
完成执行重要的事情


4

我的建议:

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

可以处理集合和数组:

bool isNullOrEmpty = array.IsNullOrEmpty()

替代

bool isNullOrEmpty = array == null || array.Length == 0;

使用 IEnumerable 替代 ICollection,使用 Any() 代替 Count,怎么样? - brickner
@brickner IEnumerable<T>.Any() - 只有泛型 IEnumerable<T> 才有 Any() 方法。 - Tadas Šukys

4
将列表转换为数据表格。
public static class DataTableConverter
{
    /// <summary>
    /// Convert a List{T} to a DataTable.
    /// </summary>
    public static DataTable ToDataTable<T>(this IList<T> items)
    {
        var tb = new DataTable(typeof(T).Name);

        PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

        foreach (PropertyInfo prop in props)
        {
            Type t = GetCoreType(prop.PropertyType);
            tb.Columns.Add(prop.Name, t);
        }

        foreach (T item in items)
        {
            var values = new object[props.Length];

            for (int i = 0; i < props.Length; i++)
            {
                values[i] = props[i].GetValue(item, null);
            }

            tb.Rows.Add(values);
        }

        return tb;
    }

    /// <summary>
    /// Determine of specified type is nullable
    /// </summary>
    public static bool IsNullable(Type t)
    {
        return !t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
    }

    /// <summary>
    /// Return underlying type if type is Nullable otherwise return the type
    /// </summary>
    public static Type GetCoreType(Type t)
    {
        if (t != null && IsNullable(t))
        {
            if (!t.IsValueType)
            {
                return t;
            }
            else
            {
                return Nullable.GetUnderlyingType(t);
            }
        }
        else
        {
            return t;
        }
    }
}

使用方法:

    IList<MyClass> myClassList = new List<MyClass>();
    DataTable myClassDataTable = myClassList.ToDataTable();

1
而且.NET已经包含了相反的功能,作为内置扩展方法(是的,听起来很奇怪,内置扩展方法):https://dev59.com/qHVC5IYBdhLWcg3wsTfv#208683 - NickAldwin

4

我不想重复别人说过的内容,所以这里有一些我使用过但尚未被提到的。 (如果太长请见谅):

public static class MyExtensions
{
    public static bool IsInteger(this string input)
    {
        int temp;

        return int.TryParse(input, out temp);
    }

    public static bool IsDecimal(this string input)
    {
        decimal temp;

        return decimal.TryParse(input, out temp);
    }

    public static int ToInteger(this string input, int defaultValue)
    {
        int temp;

        return (int.TryParse(input, out temp)) ? temp : defaultValue;
    }

    public static decimal ToDecimal(this string input, decimal defaultValue)
    {
        decimal temp;

        return (decimal.TryParse(input, out temp)) ? temp : defaultValue;
    }

    public static DateTime ToFirstOfTheMonth(this DateTime input)
    {
        return input.Date.AddDays(-1 * input.Day + 1);
    }

    // Intentionally returns 0 if the target date is before the input date.
    public static int MonthsUntil(this DateTime input, DateTime targetDate)
    {
        input = input.ToFirstOfTheMonth();

        targetDate = targetDate.ToFirstOfTheMonth();

        int result = 0;

        while (input < targetDate)
        {
        input = input.AddMonths(1);
            result++;
        }

        return result;
    }

    // Used for backwards compatibility in a system built before my time.
    public static DataTable ToDataTable(this IEnumerable input)
    {
        // too much code to show here right now...
    }
}

3

我使用的两个颜色扩展,主要用于控件开发:

public static class ColorExtensions
{
  // Gets a color that will be readable on top of a given background color
  public static Color GetForegroundColor(this Color input)
  {
    // Math taken from one of the replies to
    // https://dev59.com/jHE95IYBdhLWcg3wmvCh
    if (Math.Sqrt(input.R * input.R * .241 + input.G * input.G * .691 + input.B * input.B * .068) > 128)
      return Color.Black;
    else
      return Color.White;
  }

  // Converts a given Color to gray
  public static Color ToGray(this Color input)
  {
    int g = (int)(input.R * .299) + (int)(input.G * .587) + (int)(input.B * .114);
    return Color.FromArgb(input.A, g, g, g);
  }
}

使用方法:

Color foreColor = someBackColor.GetForegroundColor();
Color grayColor = someBackColor.ToGray();

3

在使用MVC时,如果有很多if语句,而我只关心truefalse,并在另一种情况下打印nullstring.Empty,那么我会这样做:

public static TResult WhenTrue<TResult>(this Boolean value, Func<TResult> expression)
{
    return value ? expression() : default(TResult);
}

public static TResult WhenTrue<TResult>(this Boolean value, TResult content)
{
    return value ? content : default(TResult);
}

public static TResult WhenFalse<TResult>(this Boolean value, Func<TResult> expression)
{
    return !value ? expression() : default(TResult);
}

public static TResult WhenFalse<TResult>(this Boolean value, TResult content)
{
    return !value ? content : default(TResult);
}

它让我可以将<%= (someBool) ? "print y" : string.Empty %>转换成<%= someBool.WhenTrue("print y") %>

我仅在混合了代码和HTML的视图中使用它,在代码文件中编写“较长”的版本更加清晰。


3

我没有查看整个线程,所以这可能已经在这里了:

public static class FluentOrderingExtensions
    public class FluentOrderer<T> : IEnumerable<T>
    {
        internal List<Comparison<T>> Comparers = new List<Comparison<T>>();

        internal IEnumerable<T> Source;

        public FluentOrderer(IEnumerable<T> source)
        {
            Source = source;
        }

        #region Implementation of IEnumerable

        public IEnumerator<T> GetEnumerator()
        {
            var workingArray = Source.ToArray();
            Array.Sort(workingArray, IterativeComparison);

            foreach(var element in workingArray) yield return element;
        }

        private int IterativeComparison(T a, T b)
        {
            foreach (var comparer in Comparers)
            {
                var result = comparer(a,b);
                if(result != 0) return result;
            }
            return 0;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion
    }

    public static FluentOrderer<T> OrderFluentlyBy<T,TResult>(this IEnumerable<T> source, Func<T,TResult> predicate) 
        where TResult : IComparable<TResult>
    {
        var result = new FluentOrderer<T>(source);
        result.Comparers.Add((a,b)=>predicate(a).CompareTo(predicate(b)));
        return result;
    }

    public static FluentOrderer<T> OrderFluentlyByDescending<T,TResult>(this IEnumerable<T> source, Func<T,TResult> predicate) 
        where TResult : IComparable<TResult>
    {
        var result = new FluentOrderer<T>(source);
        result.Comparers.Add((a,b)=>predicate(a).CompareTo(predicate(b)) * -1);
        return result;
    }

    public static FluentOrderer<T> ThenBy<T, TResult>(this FluentOrderer<T> source, Func<T, TResult> predicate)
        where TResult : IComparable<TResult>
    {
        source.Comparers.Add((a, b) => predicate(a).CompareTo(predicate(b)));
        return source;
    }

    public static FluentOrderer<T> ThenByDescending<T, TResult>(this FluentOrderer<T> source, Func<T, TResult> predicate)
        where TResult : IComparable<TResult>
    {
        source.Comparers.Add((a, b) => predicate(a).CompareTo(predicate(b)) * -1);
        return source;
    }
}

使用方法:

var myFluentlyOrderedList = GetABunchOfComplexObjects()
    .OrderFluentlyBy(x=>x.PropertyA)
    .ThenByDescending(x=>x.PropertyB)
    .ThenBy(x=>x.SomeMethod())
    .ThenBy(x=>SomeOtherMethodAppliedTo(x))
    .ToList();

假设所有谓词返回与它们自身可比较的类型。使用稳定排序(如MergeSort)而不是.NET内置的QuickSort效果更佳,但它提供了类似于SQL的可读的多字段排序能力(无论如何,最接近方法链)。您可以通过定义接受比较Lambda的重载来扩展此功能以适应不可比较的成员,而不是根据谓词创建它。

编辑:由于评论者获得了一些赞,因此进行一些解释:这组方法改进了基本的OrderBy()功能,允许您按重要性降序排序多个字段。一个实际的例子是按客户、发票号(或发票日期)对发票列表进行排序。其他获取数据的方法不能工作(OrderBy()使用不稳定排序,因此无法链接),或者效率低下且看起来不像你尝试做的事情。


2
这与默认的 OrderBy 和 OrderByDescending 相比有什么优势? - Agent_9191
OrderBy() 无法链接; 每次调用 OrderBy() 都会按收集类型的单个投影进行排序。如果使用的排序算法是稳定的 MergeSort,则仍然可以使其工作,但内置的排序助手是不稳定的 QuickSort,因此在按相等术语排序时不能保持相对顺序的保证。链接 OrderBy() 还将为每个 OrderBy() 运行一次 O(nlogn) 函数; 无论您比较多少术语,这组方法都只会排序一次。 - KeithS
这可以通过避免使用 ToArray() 来改进。虽然 OrderBy 不能链接,但您应该能够将所有“Comparers”链接成一个传递给单个 OrderBy 的 IComparer <T>,对吗? - scobi
好的,排序(任何算法)需要对整个可枚举对象有所了解,因为最后一个元素可能是排在有序集合中第一位的那个元素。OrderBy() 在幕后基本上就是在做我这里正在做的事情;将源可枚举对象收集到有限集合中,进行排序,然后遍历它们。 - KeithS
1
明白了。 :) 另外:这组类与.NET Framework中的Enumerable.ThenBy()和IOrderedEnumerable类型有何区别? - scobi
实际上,按照原样并不行。我在最初编写此代码时并不知道ThenBy()的存在。然而,有一些优点。因为它是自己编写的,所以更具可扩展性;例如,您可以添加OrderUsing()和ThenUsing()方法,允许传递比较而不仅仅是投影(允许基于不是IComparable的成员进行排序)。您还可以通过将Array.Sort替换为例如并行MergeSort来调整排序的性能。 - KeithS

3

通配符字符串比较:

public static bool MatchesWildcard(this string text, string pattern)
{
    int it = 0;
    while (text.CharAt(it) != 0 &&
           pattern.CharAt(it) != '*')
    {
        if (pattern.CharAt(it) != text.CharAt(it) && pattern.CharAt(it) != '?')
            return false;
        it++;
    }

    int cp = 0;
    int mp = 0;
    int ip = it;

    while (text.CharAt(it) != 0)
    {
        if (pattern.CharAt(ip) == '*')
        {
            if (pattern.CharAt(++ip) == 0)
                return true;
            mp = ip;
            cp = it + 1;
        }
        else if (pattern.CharAt(ip) == text.CharAt(it) || pattern.CharAt(ip) == '?')
        {
            ip++;
            it++;
        }
        else
        {
            ip = mp;
            it = cp++;
        }
    }

    while (pattern.CharAt(ip) == '*')
    {
        ip++;
    }
    return pattern.CharAt(ip) == 0;
}

public static char CharAt(this string s, int index)
{
    if (index < s.Length)
        return s[index];
    return '\0';
}

这是从这篇文章中的C代码直接翻译而来,因此使用了CharAt方法,在字符串末尾返回0。

if (fileName.MatchesWildcard("*.cs"))
{
    Console.WriteLine("{0} is a C# source file", fileName);
}

很好,正是我想要的 :-) - EightyOne Unite
@aboveyou00,主要是为了性能考虑。此外,正则表达式和通配符模式之间没有明显的等价关系,如果您不需要完整的正则表达式功能,则通配符更容易使用。 - Thomas Levesque

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