一个关于字符串类型扩展方法的例子
using System.Text.RegularExpressions;
namespace DemoApp
{
public static class StringExtensions
{
public static bool IsEmailAddress(this string email)
{
string pattern =
"^[a-zA-Z][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$";
return Regex.Match(email, pattern).Success;
}
public static bool IsValidIPv4(this string val)
{
if (string.IsNullOrEmpty(val))
{
return false;
}
return Regex.Match(val,
@"(?:^|\s)([a-z]{3,6}(?=://))?(://)?((?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.(?:25[0-5]|2[0-4]\d|[01]?\d\d?))(?::(\d{2,5}))?(?:\s|$)")
.Success;
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("sx123456@163.com".IsEmailAddress());
Console.WriteLine("sx123456@163".IsEmailAddress());
Console.WriteLine(".233.161.1470".IsValidIPv4());
Console.WriteLine(".233.161.147".IsValidIPv4());
}
}
}
这里例子实现对“System.string”类型的一些扩展方法,扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种静态方法,但可以像扩展类型上的实例方法一样进行调用。
常见的扩展方法——LINQ 标准查询运算符
查询功能添加到现有的 和 类型。 若要使用标准查询运算符,请先使用 using System.Linq 指令将它们置于范围中。 然后,任何实现了 的类型看起来都具有 、、 等实例方法。 在 类型的实例(如 或 )后键入“dot”时,可以在 IntelliSense 语句完成中看到这些附加方法。
如下实现排序的例子:
namespace DemoApp
{
class Program
{
static void Main(string[] args)
{
List<int> nums = new List<int> { 10, 45, 15, 39, 21, 26 };
var result = nums.OrderBy(x => x);
foreach (var i in result)
{
System.Console.Write(i + " ");
}
}
}
}
查看OrderBy源码实现
...
namespace System.Linq
{
public static partial class Enumerable
{
...
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
=> new OrderedEnumerable<TSource, TKey>(source, keySelector, null, false, null);
public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey>? comparer)
=> new OrderedEnumerable<TSource, TKey>(source, keySelector, comparer, false, null);
...
}
}
扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。 它们的第一个参数指定方法操作的类型。 该参数位于 修饰符之后。 仅当你使用 using 指令将命名空间显式导入到源代码中之后,扩展方法才位于范围中。
特定于层的功能
使用洋葱架构或其他分层应用程序设计时,通常具有一组域实体或数据传输对象,可用于跨应用程序边界进行通信。 这些对象通常不包含任何功能,或者只包含适用于应用程序的所有层的最少功能。 使用扩展方法可以添加特定于每个应用程序层的功能,而无需使用其他层中不需要的方法来向下加载对象。
using System.ComponentModel.DataAnnotations;
namespace DemoApp
{
public enum SalesReportType
{
None,
[Display(Name = "Sales Report")]
SalesReport,
[Display(Name = "Sales by store")]
SalesByStore,
[Display(Name = "Follow Up")]
OrderFollowUp,
[Display(Name = "Invoice")]
Invoice,
}
public static class SalesReportTypeExtension
{
public static string ToFileName(this SalesReportType reportTemplate)
{
switch (reportTemplate)
{
case SalesReportType.SalesReport:
return ("Sales Order Summary Report");
case SalesReportType.SalesByStore:
return ("Sales Analysys Report");
case SalesReportType.OrderFollowUp:
return ("SalesOrderFollowUp");
default:
return ("Sales Invoice");
}
}
}
public class DomainEntity
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static class DomainEntityExtensions
{
public static string FullName(this DomainEntity value)
=> $"{value.FirstName} {value.LastName}";
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(SalesReportType.OrderFollowUp.ToFileName());
Console.WriteLine(new DomainEntity() { Id = 666, FirstName = "Ma", LastName = "Jack" }.FullName());
}
}
}
提醒
尽管通过修改对象的代码来添加功能,或者在合理和可行的情况下派生新类型等方式仍是可取的,但扩展方法已成为在整个 .NET 生态系统中创建可重用功能的关键选项。 对于原始源不受控制、派生对象不合适或不可用,或者不应在功能适用范围之外公开功能的情况,扩展方法是一个不错的选择。
注意:
- 如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用。
- 在命名空间级别将扩展方法置于范围中。 例如,如果你在一个名为
Extensions 的命名空间中具有多个包含扩展方法的静态类,则这些扩展方法将全部由 using Extensions; 指令置于范围中。
参考