C# Extension Methods
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
扩展方法可以直接给现有的类扩展方法。扩展方法是静态方法,但是它们像扩展类的实例方法一样被调用。对于用C#、F#、VB写的客户端代码,调用现有类中的方法和扩展方法是没有明显区别的。例如,LINQ中的GroupBy, OrderBy, Average等方法就是IEumerable<T>类型的扩展方法。
扩展方法的第一个参数是用this关键词指定的能够调用该方法的类型。下面是微软document上给出的给string类添加扩展方法的例子。
namespace ExtensionMethod { public static class MyExtensions { public static int WordCount(this string str) { return str.Split(new char[] {' ','.','?'}, StringSplitOptions.RemoveEmptyEntries).Length; } } }
使用时,记得要引用命名空间。现在就像调用string.Count()方法一样可以直接调用string.WordCount()方法了。
using ExtensionMethod; string s = "Hello Extension Methods"; int i = s.WordCount(); Console.WriteLine(i); Console.Read();
值得注意的是,扩展方法的优先级低于类中直接定义的方法的优先级。也就是说,如果类中定义了A()方法,又在扩展方法中定义了一个A()方法,那么优先调用的是类中定义的A()方法。
扩展方法通常与Lambda表达式(或称为匿名方法,anonymous functions)一同使用。请看下方的例子(来源于《Pro ASP.NET Core6 Develop Cloud-Ready Web Applications Using MVC, Blazor, and Razor Pages》)
public class Product { public long? ProductId { get; set; } public string Name { get; set; } = String.Empty; public string Description { get; set; } = String.Empty; public decimal Price { get; set; } public string Category { get; set; } = String.Empty; } using System.Collections; public class ShoppingCart:IEnumerable<Product?> { public IEnumerable<Product?>? Products { get; set; } public IEnumerator<Product?> GetEnumerator() => Products?.GetEnumerator() ?? Enumerable.Empty<Product?>().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }
上方代码定义了两个类:Product和ShoppingCart。其中ShoppingCart继承自IEnumerable<Product?>。下面定义两个IEnumerable<Product?>类的扩展方法:用于计算购物车内所有商品的总价值的TotalPrices方法和根据特定条件过滤购物车的Filter方法。
public static class MyExtensions { public static decimal TotalPrices(this IEnumerable<Product?> products) { decimal total = 0; foreach(Product? prod in products) { total += prod?.Price ?? 0; } return total; } public static IEnumerable<Product?>Filter(this IEnumerable<Product?> productEnum, Func<Product?, bool> selector) { foreach(Product? prod in productEnum) { if (selector(prod)) { yield return prod; } } } }
使用lambda表达式调用扩展方法
using ExtensionMethod; ShoppingCart cart = new ShoppingCart() { Products = new List<Product>() { new Product { Name = "Kayak", Price = 275M }, new Product { Name = "Lifejacket", Price = 48.95M }, new Product { Name="Soccer",Price=60M } } }; decimal cartTotal = cart.TotalPrices(); decimal filterPrice = cart.Filter(p => (p?.Price > 50M)).TotalPrices(); Console.WriteLine(cartTotal); Console.WriteLine(filterPrice); Console.Read();