一个关于字符串类型扩展方法的例子
using System.Text.RegularExpressions;
namespace DemoApp
{
public static class StringExtensions
{
/// <summary>
/// 验证电子邮件地址
/// </summary>
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;
}
/// <summary>
/// 验证字符串是否有效 IPv4
/// 正则表达式取自<a href="http://regexlib.com/REDetails.aspx?regexp_id=2035">正则表达式参考</a>
/// </summary>
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()); //True
Console.WriteLine("sx123456@163".IsEmailAddress()); //False
Console.WriteLine("64.233.161.1470".IsValidIPv4()); //False
Console.WriteLine("64.233.161.147".IsValidIPv4()); //True
}
}
}
这里例子实现对“System.string
”类型的一些扩展方法,扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种静态方法,但可以像扩展类型上的实例方法一样进行调用。
常见的扩展方法——LINQ 标准查询运算符
查询功能添加到现有的 System.Collections.IEnumerable 和 System.Collections.Generic.IEnumerable<T> 类型。 若要使用标准查询运算符,请先使用 using System.Linq
指令将它们置于范围中。 然后,任何实现了 IEnumerable<T> 的类型看起来都具有 GroupBy、OrderBy、Average 等实例方法。 在 IEnumerable<T> 类型的实例(如 List<T> 或 Array)后键入“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 + " "); // 10 15 21 26 39 45
}
}
}
}
查看OrderBy源码实现
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
...
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);
...
}
}
扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。 它们的第一个参数指定方法操作的类型。 该参数位于 this 修饰符之后。 仅当你使用 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()); //SalesOrderFollowUp
Console.WriteLine(new DomainEntity() { Id = 666, FirstName = "Ma", LastName = "Jack" }.FullName()); //Ma Jack
}
}
}
提醒
尽管通过修改对象的代码来添加功能,或者在合理和可行的情况下派生新类型等方式仍是可取的,但扩展方法已成为在整个 .NET 生态系统中创建可重用功能的关键选项。 对于原始源不受控制、派生对象不合适或不可用,或者不应在功能适用范围之外公开功能的情况,扩展方法是一个不错的选择。
注意:
- 如果扩展方法与该类型中定义的方法具有相同的签名,则扩展方法永远不会被调用。
- 在命名空间级别将扩展方法置于范围中。 例如,如果你在一个名为
Extensions
的命名空间中具有多个包含扩展方法的静态类,则这些扩展方法将全部由using Extensions;
指令置于范围中。
参考
-
https://github.com/timothymugayi/StringExtensions.git
-
https://referencesource.microsoft.com/