大学学习的java,工作后转成了C#,由于工作需要,全力去学习了C#,学习中,相信大家都会有相同的疑惑,在判断一个字符串是否为空的时候总会用到string.IsNullOrEmpty(s)这个方法,最开始就想到了是不是反射,后面才知道这是c#的扩展方法,后面的内容慢慢讲解我对扩展方法由浅到深的理解与用法。
1:扩展方法的自定义实现,我这里特别用到了判断类型是否为数值类型的方法(可以去比较学习下),里面子定义了4个扩展方法
1 namespace ConsoleApplication1 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 var i = 32; 8 System.Console.WriteLine(i.GetType().IsNumericType()); 9 System.Console.ReadKey(); 10 } 11 } 12 13 public static class TestExtension 14 { 15 //老外的代码 16 public static bool IsNumeric(this Type dataType) 17 { 18 if (dataType == null) 19 throw new ArgumentNullException("dataType"); 20 21 return (dataType == typeof(int) 22 || dataType == typeof(double) 23 || dataType == typeof(long) 24 || dataType == typeof(short) 25 || dataType == typeof(float) 26 || dataType == typeof(Int16) 27 || dataType == typeof(Int32) 28 || dataType == typeof(Int64) 29 || dataType == typeof(uint) 30 || dataType == typeof(UInt16) 31 || dataType == typeof(UInt32) 32 || dataType == typeof(UInt64) 33 || dataType == typeof(sbyte) 34 || dataType == typeof(Single) 35 ); 36 } 37 /// <summary> 38 /// 判断是否为数值类型。 39 /// </summary> 40 /// <param name="t">要判断的类型</param> 41 /// <returns>是否为数值类型</returns> 42 public static bool IsNumericType(this Type t) 43 { 44 var tc = Type.GetTypeCode(t); 45 return (t.IsPrimitive && t.IsValueType && !t.IsEnum && tc != TypeCode.Char && tc != Typ eCode.Boolean) || tc == TypeCode.Decimal; 46 } 47 48 /// <summary> 49 /// 判断是否为可空数值类型。 50 /// </summary> 51 /// <param name="t">要判断的类型</param> 52 /// <returns>是否为可空数值类型</returns> 53 public static bool IsNumericOrNullableNumericType(this Type t) 54 { 55 return t.IsNumericType() || (t.IsNullableType() && t.GetGenericArguments()[0].IsNumeric Type()); 56 } 57 58 /// <summary> 59 /// 判断是否为可空类型。 60 /// 注意,直接调用可空对象的.GetType()方法返回的会是其泛型值的实际类型,用其进行此判断肯定返回false。 61 /// </summary> 62 /// <param name="t">要判断的类型</param> 63 /// <returns>是否为可空类型</returns> 64 public static bool IsNullableType(this Type t) 65 { 66 return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); 67 } 68 69 } 70 }
2:通过上面的列子可能就只能看出它使用上更为便捷而已,但当我接触到Linq,才发现扩展方法的强大,自己也学着参考改造下Linq的构造方法以增加增加所需要的功能
1 public static class QueryableExtensions 2 { 3 public static IQueryable<T> OrderBy<T>(this IQueryable<T> queryable, string propertyName) 4 { 5 return QueryableHelper<T>.OrderBy(queryable, propertyName, false); 6 } 7 public static IQueryable<T> OrderBy<T>(this IQueryable<T> queryable, string propertyName, b ool desc) 8 { 9 return QueryableHelper<T>.OrderBy(queryable, propertyName, desc); 10 } 11 12 static class QueryableHelper<T> 13 { 14 //为了缓存表达式树 15 private static Dictionary<string, LambdaExpression> cache = new Dictionary<string, Lamb daExpression>(); 16 public static IQueryable<T> OrderBy(IQueryable<T> queryable, string propertyName, bool desc) 17 { 18 19 dynamic keySelector = GetLambdaExpression(propertyName); 20 return desc ? Queryable.OrderByDescending(queryable, keySelector) : Queryable.Order By(queryable, keySelector); 21 } 22 private static LambdaExpression GetLambdaExpression(string propertyName) 23 { 24 if (cache.ContainsKey(propertyName)) return cache[propertyName]; 25 var param = Expression.Parameter(typeof(T)); 26 var body = Expression.Property(param, propertyName); 27 var keySelector = Expression.Lambda(body, param); 28 cache[propertyName] = keySelector; 29 return keySelector; 30 } 31 } 32 }
4:对于数据库的操作,也可以使用扩展方法,我上篇随笔也说了,项目用了IOC,没有在数据库这块用到扩展方法,这可以参考http://www.cnblogs.com/xiaotuni/archive/2012/05/29/2523683.html
5:当使用到MVC框架的时候,看到前端的页面使用Html.BeginForm等一些相同格式语法的时候,我马上意识到这就是MVC里面的扩展方法,转而想是否可以自己也写一些扩展方法,方便自己使用,首先我想到前端Table的遍历,分页等,并着手全部实现,并应用于项目中
Table的遍历,我们在后台写好一个扩展方法类
1 public static class RazorExtensions 2 { 3 public static HelperResult /*List*/ Repeat<T>(this IEnumerable<T> items, 4 Func<T, HelperResult> template) 5 { 6 if (items == null) 7 return new HelperResult(t => t.Write("")); 8 9 return new HelperResult(writer => 10 { 11 foreach (var item in items) 12 { 13 template(item).WriteTo(writer); 14 } 15 }); 16 } 17 }
前端我们引用他的命名空间,如下方式调用:
<tbody> @Model.Repeat( @<tr> <td><ahref="/order/catalog/new/@item.ContractNo">@item.ContractNo</a></td> <td>@item.StrokeNo</td> <td>@item.StrokeDesc</td> <td>@item.ContractStatus</td> <td>@item.DeptNo</td> <td>@item.Season</td> <td>@item.SupplierSeries</td> <td>@item.CountryCode</td> <td>@item.ProductDesc</td> <td>@item.DateLastModified</td> <td>@item.CreatedTime.Format()</td> </tr>) </tbody>
分页控件的实现:
后台:首先我们必须有一个page类
namespace WebPrint.Web.Mvc.Pager { public class PagerOptions { public PagerOptions() { AutoHide = true; PageIndexParameterName = "pageindex"; NumericPagerItemCount = 10; AlwaysShowFirstLastPageNumber = false; ShowPrevNext = true; NextPageText = ">"; PrevPageText = "<"; ShowNumericPagerItems = true; ShowFirstLast = true; FirstPageText = "<<"; LastPageText = ">>"; ShowMorePagerItems = true; MorePageText = "..."; ShowDisabledPagerItems = true; PagerItemsSeperator = " "; ContainerTagName = "div"; InvalidPageIndexErrorMessage = "页索引无效"; PageIndexOutOfRangeErrorMessage = "页索引超出范围"; MaxPageIndex = 0; FirstPageRouteName = null; } /// <summary> /// 首页使用的路由名称(无页索引参数) /// </summary> public string FirstPageRouteName { get; set; } /// <summary> /// 当总页数只有一页时是否自动隐藏 /// </summary> public bool AutoHide { get; set; } /// <summary> /// 页索引超出范围时显示的错误消息 /// </summary> public string PageIndexOutOfRangeErrorMessage { get; set; } /// <summary> /// 页索引无效时显示的错误消息 /// </summary> public string InvalidPageIndexErrorMessage { get; set; } /// <summary> /// url中页索引参数的名称 /// </summary> public string PageIndexParameterName { get; set; } private string _containerTagName; /// <summary> /// 分页控件html容器标签名,默认为div /// </summary> public string ContainerTagName { get { return _containerTagName; } set { if (string.IsNullOrEmpty(value)) throw new System.ArgumentException("ContainerTagName不能为null或空字符串", "ContainerTagName"); _containerTagName = value; } } /// <summary> /// whether or not show first and last numeric page number /// </summary> public bool AlwaysShowFirstLastPageNumber { get; set; } /// <summary> /// 显示的最大数字页索引按钮数 /// </summary> public int NumericPagerItemCount { get; set; } /// <summary> /// 是否显示上页和下页 /// </summary> public bool ShowPrevNext { get; set; } /// <summary> /// 上一页文本 /// </summary> public string PrevPageText { get; set; } /// <summary> /// 下一页文本 /// </summary> public string NextPageText { get; set; } /// <summary> /// 是否显示数字页索引按钮及更多页按钮 /// </summary> public bool ShowNumericPagerItems { get; set; } /// <summary> /// 是否显示第一页和最后一页 /// </summary> public bool ShowFirstLast { get; set; } /// <summary> /// 第一页文本 /// </summary> public string FirstPageText { get; set; } /// <summary> /// 最后一页文本 /// </summary> public string LastPageText { get; set; } /// <summary> /// 是否显示更多页按钮 /// </summary> public bool ShowMorePagerItems { get; set; } /// <summary> /// 更多页按钮文本 /// </summary> public string MorePageText { get; set; } /// <summary> /// 包含分页控件的父容器标签的ID /// </summary> public string Id { get; set; } /// <summary> /// CSS样式类 /// </summary> public string CssClass { get; set; } /// <summary> /// whether or not show disabled navigation buttons /// </summary> public bool ShowDisabledPagerItems { get; set; } /// <summary> /// 分页元素之间的分隔符,默认为两个html空格( ) /// </summary> public string PagerItemsSeperator { get; set; } /// <summary> /// 限制显示的最大页数,默认值为0,即根据总记录数算出的总页数 /// </summary> public int MaxPageIndex { get; set; } } }
2个生成分页控件HTML代码的类
using System; using System.Web.Mvc; using System.Web.Routing; using WebPrint.Model.Helper; namespace WebPrint.Web.Mvc.Pager { public static class PagerHelper { #region Html Pager private static MvcHtmlString Pager(this HtmlHelper helper, int totalItemCount, int pageSize, int pageIndex, string actionName, string controllerName, PagerOptions pagerOptions, string routeName, object routeValues, object htmlAttributes) { var totalPageCount = (int) Math.Ceiling(totalItemCount/(double) pageSize); var builder = new PagerBuilder ( helper, actionName, controllerName, totalItemCount, totalPageCount, pageIndex, pageSize, pagerOptions, routeName, new RouteValueDictionary(routeValues), new RouteValueDictionary(htmlAttributes) ); return builder.RenderPager(); } public static MvcHtmlString Pager(this HtmlHelper helper, IPagedList pagedList, PagerOptions pagerOptions) { //if (pagedList == null) // return Pager(helper, pagerOptions, null); return Pager(helper, pagedList.RecordsCount, pagedList.PageSize, pagedList.PageIndex, null, null, pagerOptions, null, null, null); } #endregion } } /* ASP.NET MvcPager 分页组件 [email protected]杨涛\Webdiyer (http://www.webdiyer.com) Source code released under Ms-PL license */ using System; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Web; using System.Web.Mvc; using System.Web.Routing; namespace WebPrint.Web.Mvc.Pager { internal class PagerBuilder { private readonly HtmlHelper html; private readonly string actionName; private readonly string controllerName; private readonly int totalPageCount = 1; private readonly int totalRecordCount = 1; private readonly int pageIndex; private readonly int pageSize; private readonly PagerOptions pagerOptions; private readonly RouteValueDictionary routeValues; private readonly string routeName; private readonly int startPageIndex = 1; private readonly int endPageIndex = 1; private IDictionary<string, object> htmlAttributes; private const string CopyrightText = "\r\n<!--MvcPager v2.0 for ASP.NET MVC 4.0+ © 2009-2013 Webdiyer (http://www.webdiyer.com)-->\r\n"; /// <summary> /// 适用于PagedList为null时 /// </summary> //internal PagerBuilder(HtmlHelper html,PagerOptions pagerOptions, IDictionary<string, object> htmlAttributes) //{ // if (pagerOptions == null) // pagerOptions = new PagerOptions(); // this.html = html; // this.pagerOptions = pagerOptions; // this.htmlAttributes = htmlAttributes; //} internal PagerBuilder(HtmlHelper html, string actionName, string controllerName, int totalRecordCount, int totalPageCount, int pageIndex, int pageSize, PagerOptions pagerOptions, string routeName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes) { if (pagerOptions == null) pagerOptions = new PagerOptions(); this.html = html; this.actionName = actionName; this.controllerName = controllerName; this.totalRecordCount = totalRecordCount; if (pagerOptions.MaxPageIndex == 0 || pagerOptions.MaxPageIndex > totalPageCount) this.totalPageCount = totalPageCount; else this.totalPageCount = pagerOptions.MaxPageIndex; this.pageIndex = pageIndex; this.pageSize = pageSize; this.pagerOptions = pagerOptions; this.routeName = routeName; this.routeValues = routeValues; this.htmlAttributes = htmlAttributes; // start page index startPageIndex = pageIndex - (pagerOptions.NumericPagerItemCount/2); if (startPageIndex + pagerOptions.NumericPagerItemCount > this.totalPageCount) startPageIndex = this.totalPageCount + 1 - pagerOptions.NumericPagerItemCount; if (startPageIndex < 1) startPageIndex = 1; // end page index endPageIndex = startPageIndex + this.pagerOptions.NumericPagerItemCount - 1; if (endPageIndex > this.totalPageCount) endPageIndex = this.totalPageCount; } private void AddPrevious(ICollection<PagerItem> results) { var item = new PagerItem(pagerOptions.PrevPageText, pageIndex - 1, pageIndex == 1, PagerItemType.PrevPage); if (!item.Disabled || (item.Disabled && pagerOptions.ShowDisabledPagerItems)) results.Add(item); } private void AddFirst(ICollection<PagerItem> results) { var item = new PagerItem(pagerOptions.FirstPageText, 1, pageIndex == 1, PagerItemType.FirstPage); //只有导航按钮未被禁用,或导航按钮被禁用但ShowDisabledPagerItems=true时才添加 if (!item.Disabled || (item.Disabled && pagerOptions.ShowDisabledPagerItems)) results.Add(item); } private void AddMoreBefore(ICollection<PagerItem> results) { if (startPageIndex > 1 && pagerOptions.ShowMorePagerItems) { var index = startPageIndex - 1; if (index < 1) index = 1; var item = new PagerItem(pagerOptions.MorePageText, index, false, PagerItemType.MorePage); results.Add(item); } } private void AddPageNumbers(ICollection<PagerItem> results) { for (var pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++) { var text = pageIndex.ToString(CultureInfo.InvariantCulture); //if (pageIndex == this.pageIndex && !string.IsNullOrEmpty(pagerOptions.CurrentPageNumberFormatString)) // text = String.Format(pagerOptions.CurrentPageNumberFormatString, text); //else if (!string.IsNullOrEmpty(pagerOptions.PageNumberFormatString)) // text = String.Format(pagerOptions.PageNumberFormatString, text); var item = new PagerItem(text, pageIndex, false, PagerItemType.NumericPage); results.Add(item); } } private void AddMoreAfter(ICollection<PagerItem> results) { if (endPageIndex < totalPageCount) { var index = startPageIndex + pagerOptions.NumericPagerItemCount; if (index > totalPageCount) { index = totalPageCount; } var item = new PagerItem(pagerOptions.MorePageText, index, false, PagerItemType.MorePage); results.Add(item); } } private void AddNext(ICollection<PagerItem> results) { var item = new PagerItem(pagerOptions.NextPageText, pageIndex + 1, pageIndex >= totalPageCount, PagerItemType.NextPage); if (!item.Disabled || (item.Disabled && pagerOptions.ShowDisabledPagerItems)) results.Add(item); } private void AddLast(ICollection<PagerItem> results) { var item = new PagerItem(pagerOptions.LastPageText, totalPageCount, pageIndex >= totalPageCount, PagerItemType.LastPage); if (!item.Disabled || (item.Disabled && pagerOptions.ShowDisabledPagerItems)) results.Add(item); } /// <summary> /// 根据页索引生成分页导航Url /// </summary> /// <param name="pageIndex">要生成导航链接的页索引</param> /// <returns>分页导航链接Url</returns> private string GenerateUrl(int pageIndex) { ViewContext viewContext = html.ViewContext; //若要生成url的页索引小于1或大于总页数或等于当前页索引时,无需生成分页导航链接 if (pageIndex > totalPageCount || pageIndex == this.pageIndex) return null; var routeValues1 = new RouteValueDictionary(viewContext.RouteData.Values); AddQueryStringToRouteValues(routeValues1, viewContext); if (this.routeValues != null && this.routeValues.Count > 0) { foreach (var de in this.routeValues) { if (!routeValues1.ContainsKey(de.Key)) { routeValues1.Add(de.Key, de.Value); } else { routeValues1[de.Key] = de.Value; //手动添加的RouteValues具有高优先级 } } } var pageValue = viewContext.RouteData.Values[pagerOptions.PageIndexParameterName]; string routeName = this.routeName; // 设置Route Value中的分页导航Url参数值,pageIndex为0时生成适用于脚本的导航链接 if (pageIndex == 0) routeValues1[pagerOptions.PageIndexParameterName] = "__" + pagerOptions.PageIndexParameterName + "__"; else { if (pageIndex == 1) { if (!string.IsNullOrWhiteSpace(pagerOptions.FirstPageRouteName)) //如果显式指定了FirstPageRouteName,则使用此Route { routeName = pagerOptions.FirstPageRouteName; routeValues1.Remove(pagerOptions.PageIndexParameterName); //去除页索引参数 viewContext.RouteData.Values.Remove(pagerOptions.PageIndexParameterName); } else { var curRoute = viewContext.RouteData.Route as Route; //判断当前Route是否为Route类型,如果是,则判断该Route中页索引参数默认值是否为UrlParameter.Optional,或页索引参数是否存在于该Route Url的参数列表中,如果参数默认值为UrlParameter.Optional或分页参数名不存在于Route Url参数中,则从当前的RouteValue列表中去除该参数 if (curRoute != null && (curRoute.Defaults[pagerOptions.PageIndexParameterName] == UrlParameter.Optional || !curRoute.Url.Contains("{" + pagerOptions.PageIndexParameterName + "}"))) { routeValues1.Remove(pagerOptions.PageIndexParameterName); //去除页索引参数 viewContext.RouteData.Values.Remove(pagerOptions.PageIndexParameterName); } else { routeValues1[pagerOptions.PageIndexParameterName] = pageIndex; } } } else { routeValues1[pagerOptions.PageIndexParameterName] = pageIndex; } } var routes = html.RouteCollection; string url; if (!string.IsNullOrEmpty(routeName)) url = UrlHelper.GenerateUrl(routeName, actionName, controllerName, routeValues1, routes, viewContext.RequestContext, false); else url = UrlHelper.GenerateUrl(null, actionName, controllerName, routeValues1, routes, viewContext.RequestContext, false); if (pageValue != null) viewContext.RouteData.Values[pagerOptions.PageIndexParameterName] = pageValue; return url; } /// <summary> /// 生成最终的分页Html代码 /// </summary> /// <returns></returns> internal MvcHtmlString RenderPager() { //return null if total page count less than or equal to 1 if (totalPageCount <= 1 && pagerOptions.AutoHide) return MvcHtmlString.Create(CopyrightText); //Display error message if pageIndex out of range if ((pageIndex > totalPageCount && totalPageCount > 0) || pageIndex < 1) { return MvcHtmlString.Create(string.Format("{0}<div style=\"color:red;font-weight:bold\">{1}</div>{0}", CopyrightText, pagerOptions.PageIndexOutOfRangeErrorMessage)); } var pagerItems = new List<PagerItem>(); //First page if (pagerOptions.ShowFirstLast) AddFirst(pagerItems); // Prev page if (pagerOptions.ShowPrevNext) AddPrevious(pagerItems); if (pagerOptions.ShowNumericPagerItems) { if (pagerOptions.AlwaysShowFirstLastPageNumber && startPageIndex > 1) pagerItems.Add(new PagerItem("1", 1, false, PagerItemType.NumericPage)); // more page before numeric page buttons if (pagerOptions.ShowMorePagerItems && ((!pagerOptions.AlwaysShowFirstLastPageNumber && startPageIndex > 1) || (pagerOptions.AlwaysShowFirstLastPageNumber && startPageIndex > 2))) AddMoreBefore(pagerItems); // numeric page AddPageNumbers(pagerItems); // more page after numeric page buttons if (pagerOptions.ShowMorePagerItems && ((!pagerOptions.AlwaysShowFirstLastPageNumber && endPageIndex < totalPageCount) || (pagerOptions.AlwaysShowFirstLastPageNumber && totalPageCount > endPageIndex + 1))) AddMoreAfter(pagerItems); if (pagerOptions.AlwaysShowFirstLastPageNumber && endPageIndex < totalPageCount) pagerItems.Add(new PagerItem(totalPageCount.ToString(CultureInfo.InvariantCulture), totalPageCount, false, PagerItemType.NumericPage)); } // Next page if (pagerOptions.ShowPrevNext) AddNext(pagerItems); //Last page if (pagerOptions.ShowFirstLast) AddLast(pagerItems); var sb = new StringBuilder(); foreach (PagerItem item in pagerItems) { sb.Append(GeneratePagerElement(item)); } var tb = new TagBuilder(pagerOptions.ContainerTagName); if (!string.IsNullOrEmpty(pagerOptions.Id)) tb.GenerateId(pagerOptions.Id); if (!string.IsNullOrEmpty(pagerOptions.CssClass)) tb.AddCssClass(pagerOptions.CssClass); tb.MergeAttributes(htmlAttributes, true); sb.Length -= pagerOptions.PagerItemsSeperator.Length; var startRecords = ((pageIndex - 1)*pageSize) + 1; var endRecords = pageIndex*pageSize; if (endRecords > totalRecordCount) endRecords = totalRecordCount; var recordsCountText = string.Format("<span class=\"summary\">{0} - {1}, {2} records</span>", startRecords, endRecords, totalRecordCount); tb.InnerHtml = sb.ToString() + recordsCountText; return MvcHtmlString.Create(CopyrightText + /*pagerScript +*/ tb.ToString(TagRenderMode.Normal) + CopyrightText); } private MvcHtmlString GeneratePagerElement(PagerItem item) { //pager item link string url = GenerateUrl(item.PageIndex); if (item.Disabled) //first,last,next or previous page return CreateWrappedPagerElement(item, String.Format("<a class=\"disabled\" disabled=\"disabled\">{0}</a>", item.Text)); return CreateWrappedPagerElement(item, string.IsNullOrEmpty(url) ? HttpUtility.HtmlEncode(item.Text) : String.Format("<a href=\"{0}\">{1}</a>", url, item.Text)); } private MvcHtmlString CreateWrappedPagerElement(PagerItem item, string el) { string navStr = el; switch (item.Type) { case PagerItemType.FirstPage: case PagerItemType.LastPage: case PagerItemType.NextPage: case PagerItemType.PrevPage: break; case PagerItemType.MorePage: break; case PagerItemType.NumericPage: if (item.PageIndex == pageIndex) { navStr = string.Format("<a href=\"javascript:void(0);\" class=\"current\">{0}</a>", el); } break; } return MvcHtmlString.Create(navStr + pagerOptions.PagerItemsSeperator); } private void AddQueryStringToRouteValues(RouteValueDictionary routeValues, ViewContext viewContext) { if (routeValues == null) routeValues = new RouteValueDictionary(); var rq = viewContext.HttpContext.Request.QueryString; if (rq != null && rq.Count > 0) { var invalidParams = new[] {"x-requested-with", "xmlhttprequest", pagerOptions.PageIndexParameterName.ToLower()}; foreach (string key in rq.Keys) { // 添加url参数到路由中 if (!string.IsNullOrEmpty(key) && Array.IndexOf(invalidParams, key.ToLower()) < 0) { var kv = rq[key]; routeValues[key] = kv; } } } } } }
使用扩展方法:
public static MvcHtmlString Paginaton(this HtmlHelper helper, IPagedList pagedList) { var pagerOptions = new PagerOptions { PageIndexParameterName = "page", CssClass = "pagination", ShowFirstLast = false, AlwaysShowFirstLastPageNumber = true, ShowMorePagerItems = true, NumericPagerItemCount = 5, PagerItemsSeperator = string.Empty, }; return helper.Pager(pagedList, pagerOptions); }
前端直接引用调用:
<div class="pagination_div"> @Html.Paginaton(Model) </div>
总结:相对于鹤冲天大哥的见解,我对于扩张方法的使用差的太远,但一般的场景也可以用到扩展方法的地方都还是可以轻松应对,所以我们平常就该对深入的学习认识他,才能在后面应用自如
时间: 2024-10-13 17:40:29