学前篇之扩展方法、链式编程

前言

目的没有别的,就是介绍几点在ASP.NETMVC 用到C#语言特性,还有一些其他琐碎的知识点,强行的划分一个范围的话,只能说都跟MVC有关,有的是外围的知识,有的是包含在框架内的。 MVC学前篇字样?有噱头的成分也有真实的成分,所以工欲善其事,必先利其器。器是什么?基础嘛,虽然说MVC框架中涉及到的知识很多很多也不是我一篇两篇能说完的,我能做的就是知道多少就跟大家分享多少,当然了随着时间的推移会完善这个系列。

1扩展方法

扩展方法是C# 3.0特性里的知识,它用在最多的地方是在Linq中,把众多的查询功能添加到了IEnumerable和IEnumerable<T>类型中, 这里不说太多,不然就扯Linq了。

运用的实际场景:有个购物清单(购物车)对象,它包含有添加、删除清单内物品的功能

商品对象,它包含 商品名称、商品价格两个属性

代码1-1

 namespace BlogCase
     public class Commodity
     {
         public string Name { get; set; }
         public float Price { get; set; }
     }
     
     namespace BlogCase
     /// <summary>
     /// 购物清单
     /// </summary>
     public class ShoppingList
     {
         private List<Commodity> _Commodities;
 
         public List<Commodity> Commodities
         {
             get { return _Commodities; }
         }
 
         public ShoppingList()
         {
             _Commodities = new List<Commodity>();
         }
 
         public bool AddCommodity(Commodity commodity)
         {
             _Commodities.Add(commodity);
             return true;
         }
 
         public bool RemoveCommodity(Commodity commodity)
         {
             if (_Commodities.Contains(commodity))
             {
                 _Commodities.Remove(commodity);
                 return true;
             }
             else
             {
                 return false;
             }
         }
      }

然后在这个令人不安、惶恐、期待、兴奋的新需求来了,要求清单可以提供一个清单内部所有货品价格的合计而又不破坏对象结构. 对的。这是非常合理的需求,现在咒骂也于事无补,因为需求总是未知的。在焦急、无奈中曙光就降临了,它就是C# 3.0中的特性扩展方法。

来看代码1-2

代码1-2

 using BlogCase;
 using System.Linq;
 
 namespace BlogCase.Extension
 {
     public static class ShoppingListExtension
     {
         public static float Total(this ShoppingList shoppintlist)
         {
             return shoppintlist.Commodities.Sum(commodity => commodity.Price);
         }
     }
 }

这里要说的是ShoppingListExtension类型是一个静态类,里面定义了一个静态方法Total,方法的签名是ShoppingList类型的参数,唯一不同的是在ShoppingList类型前面多了个this关键字,这时对ShoppingList类型的扩展方法已经定义好了。来看下示例代码1-3的使用吧。

代码1-3

 using BlogCase.Extension;
 
 namespace BlogCase
 { 
     class Program
     {
         static void Main(string[] args)
         {
             ShoppingList shoppinglistTest = new ShoppingList();
             shoppinglistTest.AddCommodity(new Commodity() { Name = "A", Price = 14.3f });
             shoppinglistTest.AddCommodity(new Commodity() { Name = "B", Price =  });
             shoppinglistTest.AddCommodity(new Commodity() { Name = "C", Price = 27.9f });
             shoppinglistTest.AddCommodity(new Commodity() { Name = "D", Price = 34.3f });
             Console.WriteLine(shoppinglistTest.Total().ToString());
             Console.ReadLine();
         }
     }
 }

这里需要注意的是需要引用扩展方法类ShoppingListExtension所在的命名空间,在VS开发环境中,扩展方法的图标也和普通方法的不一样。如图1

图1

运行一下代码1-3,结果如图2。

图2

2链式编程思想

上面的内容是对类型来做扩展,添加了个扩展方法,这样对象间的耦合还是比较大的,假使修改了ShoppingList的内部结构,那ShoppingListExtension那里也得做相对应的修改,这里就要提到为什么要面相抽象编程了,后续的篇幅会提到。 现在我们要做的就是把ShoppingListExtension中的依赖类型换位更高层次的类型,并且再添加个扩展方法,用于过滤一些数据。由此全部结合起来可以看到一个链式编程的模型,对于学习linq和一些其他知识,这都是一个好的铺垫。

看下代码2-1示例,这是修改后的ShoppingList类型。

代码2-1

 namespace BlogCase
 {
     public class ShoppingList : IEnumerable<Commodity>, IEnumerator<Commodity>
     {
         private List<Commodity> _Commodities = new List<Commodity>();
         public void Add(Commodity commodity)
         {
             _Commodities.Add(commodity);
         }
         public IEnumerator<Commodity> GetEnumerator()
         {
             return this;
         }
         IEnumerator IEnumerable.GetEnumerator()
         {
             return this;
         }
         private int _index = -;
         public Commodity Current
         {
             get
             {
                 return _Commodities[_index];
             }
         }
         public void Dispose()
         {
 
         }
         object System.Collections.IEnumerator.Current
         {
             get
             {
                 return Current;
             }
         }
         public bool MoveNext()
         {
             if (_index < _Commodities.Count)
             {
                 _index++;
             }
             return (!(_index == _Commodities.Count));
         }
         public void Reset()
         {
             _index = -;
         }
     }
 }

重新定义了ShoppingList类型,让其实现IEnumerable<Commodity>接口类型和IEnumerator<Commodity>接口类型,为什么要实现IEnumerable<Commodity>接口类型呢?因为示例代码中也要用到Linq的扩展方法,那些扩展方法是基于IEnumerable<T>类型扩展的,在第一部分中已经使用到了,细心的朋友可能已经留意到了。

对ShoppingListExtension类型也稍作修改,添加了新的扩展方法,并且修改了之前的扩展方法,达到了消耦的目的。

代码2-2

 using BlogCase;
 using System.Linq;
 namespace BlogCase.Extension
 {
     public static class ShoppingListExtension
     {
         public static float Total(this IEnumerable<Commodity> shoppintlist)
         {
             return shoppintlist.Sum(commodity => commodity.Price);
         }
 
         public static IEnumerable<Commodity> Filter(this IEnumerable<Commodity> shoppinglist, Func<Commodity, bool> commodityFilter)
         {
             var commodities = shoppinglist.Where(commodityFilter);
             return commodities;
         }
     }
 }

修改后的ShoppingListExtension类中,Total扩展方法的针对的类型变换掉了,而在Filter扩展方法中把返回类型定义为IEnumerable<Commodity>类型,并且定义了一个参数,参数类型为Func<Commodity, bool> 的委托,完全可以用lambda表达式代替,对于lambda的知识后续的篇幅中会有说到。都做了修改后,那我们来看一下测试时的代码。

代码2-3

 using BlogCase.Extension;
 namespace BlogCase
 {
     class Program
     {
         static void Main(string[] args)
         {
             ShoppingList shoppinglistTest = new ShoppingList();
             shoppinglistTest.Add(new Commodity() { Name = "A", Price = 50.3f });
             shoppinglistTest.Add(new Commodity() { Name = "B", Price = 60 });
             shoppinglistTest.Add(new Commodity() { Name = "C", Price = 70.9f });
             shoppinglistTest.Add(new Commodity() { Name = "D", Price = 80.3f });
 
             Console.WriteLine(shoppinglistTest.Filter(commodity=>commodity.Price>58).Total().ToString());
             Console.ReadLine();
         }
      }
 }

运行结果如图3

图3

从shoppinglistTest变量调用扩展方法Filter时,传入了一个查询条件(货品加个大于58)这个扩展方法返回的就是上面说的IEnumerable<Commodity>类型,然后紧接着调用IEnumerable<Commodity>类型的扩展方法Total。

到这里简单的一个链式编程模型就出来了,有兴趣的朋友可以接着去深入的了解linq,当然在此之前看完我的后续文章很重要。

ASP.NET MVC 学习