打开VS,创建新项目,选择模板ASP.NET Web应用程序 ,项目名称为LanguageFeatures ,选择MVC的空模板。
右键项目,添加引用,在程序集中找到System.Net.Http 并添加到项目。
自动属性 在Models文件夹 添加Product 类,其中有自动属性和规则属性。
namespace LanguageFeatures.Models { public class Product { private string name; public int ProductID { get ; set ; } public string Name { get { return ProductID + name; } set { name = value ; } } public string Description { get ; set ; } public decimal Price { get ; set ; } public string Category { get ; set ; } } }
添加HomeController ,添加AutoProperty动作方法 。
using LanguageFeatures.Models;using System.Web.Mvc;namespace LanguageFeatures.Controllers { public class HomeController : Controller { public string Index ( ) { return "Navigate to a URL to show an example" ; } public ViewResult AutoProperty ( ) { Product myProduct = new Product(); myProduct.Name = "Kayak" ; string productName = myProduct.Name; return View("Result" , (object )string .Format("Product name: {0}" , productName)); } } }
添加Result 视图。
@model String @{ Layout = null; } <!DOCTYPE html> <html > <head > <meta name ="viewport" content ="width=device-width" /> <title > Result</title > </head > <body > <div > @Model </div > </body > </html >
运行项目并导航到/Home/AutoProperty ,显示的结果为:Product name: 0Kayak
对象与集合的初始化器 在HomeController使用对象初始化器。
public ViewResult CreateProduct ( ) { Product myProduct = new Product { ProductID = 100 , Name = "Kayak" , Description = "A boat for one person" , Price = 275 M, Category = "Watersport" }; return View("Result" , (object )string .Format("Category: {0}" , myProduct.Category)); }
在HomeController初始化两个泛型集合和一个数组。
public ViewResult CreateCollection ( ) { string [] stringArray = { "apple" , "orange" , "plum" }; List<int > intList = new List<int > { 10 , 20 , 30 , 40 }; Dictionary<string , int > myDict = new Dictionary<string , int > { { "apple" , 10 }, { "orange" , 20 }, { "plum" , 30 } }; return View("Result" , (object )stringArray[1 ]); }
扩展方法 扩展方法是指,给那些不是你拥有的、不能直接修改的类添加方法的一种方便的办法 。
在Models文件夹添加ShoppingCar 类。
public class ShoppingCart { public List<Product> Products { get ; set ; } }
假设需要知道ShoppingCart类中Product 对象的总值,但又不能修改ShoppingCart类 ,可以使用扩展方法实现功能。
在Models文件夹添加MyExtensionMethods 类。
public static class MyExtensionMethods { public static decimal TotalPrices (this ShoppingCart cartParam ) { decimal total = 0 ; foreach (Product prod in cartParam.Products) { total += prod.Price; } return total; } }
注意 :this关键字 把TotalPrices标记为扩展方法 ,第一个参数告诉.NET扩展的是ShoppingCart 类,可以通过cartParam 参数来引用ShoppingCart类的实例。
在HomeController中使用扩展方法。
public ViewResult UseExtension ( ) { ShoppingCart cart = new ShoppingCart { Products = new List<Product> { new Product { Name = "Kayak" , Price = 275 M }, new Product { Name = "Lifejacket" , Price = 48.95 M }, new Product { Name = "Soccer ball" , Price = 19.50 M }, new Product { Name = "Corner flag" , Price = 34.95 M } } }; decimal cartTotal = cart.TotalPrices(); return View("Result" , (object )string .Format("Total: {0:c}" , cartTotal)); }
枚举器和可枚举类型 枚举器(enumerator) :枚举器对象可以依次返回请求的数组中的元素。枚举器知道项的次序并跟踪它在序列中的位置。枚举器实现了IEnumerator接口 。
IEnumerator接口 :包含3个函数成员,Current(只读属性) 、MoveNext 、Reset ,实现了IEnumerator接口的类称为枚举器 。
可枚举类型 :是指实现了IEnumerable接口的类 。
IEnumerable接口 :只有一个成员,GetEnumerator方法 ,它返回对象的枚举器。
IEnumerable<T>接口 :GetEnumerator方法返回实现IEnumerator<T> 的枚举器类的实例。
IEnumerator<T>接口 :实现该接口的类实现了Current属性,它返回实际类型的实例,而不是object引用。
对接口运用扩展方法 可以创建运用于一个接口的扩展方法,并允许在实现这个接口的所有类上调用这个扩展方法 。
在ShoppingCart类实现IEnumerable<Product>接口。
public class ShoppingCart : IEnumerable<Product> { public List<Product> Products { get ; set ; } public IEnumerator<Product> GetEnumerator ( ) { return Products.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } }
修改扩展方法,使之应用于接口上。
public static decimal TotalPrices (this IEnumerable<Product> productEnum ) { decimal total = 0 ; foreach (Product prod in productEnum) { total += prod.Price; } return total; }
在HomeController中运用扩展方法。
public ViewResult UseExtensionEnumerable ( ) { IEnumerable<Product> products = new ShoppingCart { Products = new List<Product> { new Product { Name = "Kayak" , Price = 275 M }, new Product { Name = "Lifejacket" , Price = 48.95 M }, new Product { Name = "Soccer ball" , Price = 19.50 M }, new Product { Name = "Corner flag" , Price = 34.95 M } } }; Product[] productArray = { new Product { Name = "Kayak" , Price = 275 M }, new Product { Name = "Lifejacket" , Price = 48.95 M }, new Product { Name = "Soccer ball" , Price = 19.50 M }, new Product { Name = "Corner flag" , Price = 34.95 M } }; decimal cartTotal = products.TotalPrices(); decimal arrayTotal = productArray.TotalPrices(); return View("Result" , (object )string .Format("Cart Total: {0}, Array Total: {1}" , cartTotal, arrayTotal)); }
创建过滤扩展方法
过滤扩展方法,可以对对象集合 进行过滤 。
对IEnumerable<T> 进行操作,返回一个IEnumerable<T> 结果。
用yield 关键字把选择条件运用于数据源中的数据项,以产生一个结果集。
在MyExtensionMethods类中增加过滤扩展方法。
public static IEnumerable<Product> FilterByCategory (this IEnumerable<Product> productEnum, string categoryParam ) { foreach (Product prod in productEnum) { if (prod.Category == categoryParam) { yield return prod; } } }
在HomeController中使用过滤扩展方法。
public ViewResult UseFilterExtensionMethod ( ) { IEnumerable<Product> products = new ShoppingCart { Products = new List<Product> { new Product { Name = "Kayak" , Category = "Watersports" , Price = 275 M }, new Product { Name = "Lifejacket" , Category = "Watersports" , Price = 48.95 M }, new Product { Name = "Soccer ball" , Category = "Soccer" , Price = 19.50 M }, new Product { Name = "Corner flag" , Category = "Soccer" , Price = 34.95 M } } }; decimal total = 0 ; foreach (Product prod in products.FilterByCategory("Soccer" )) { total += prod.Price; } return View("Result" , (object )string .Format("Total: {0}" , total)); }
lambda表达式 在MyExtensionMethods类中添加Filter扩展方法 ,以一个Func委托 作为参数。
public static IEnumerable<Product> Filter (this IEnumerable<Product> productEnum, Func<Product, bool > selectParam ) { foreach (Product prod in productEnum) { if (selectParam(prod)) { yield return prod; } } }
在HomeController中使用带有Func的过滤扩展方法 。这样就可以用委托中指定的任何条件来过滤Product对象。
public ViewResult UseFilterExtensionMethod ( ) { IEnumerable<Product> products = new ShoppingCart { Products = new List<Product> { new Product { Name = "Kayak" , Category = "Watersports" , Price = 275 M }, new Product { Name = "Lifejacket" , Category = "Watersports" , Price = 48.95 M }, new Product { Name = "Soccer ball" , Category = "Soccer" , Price = 19.50 M }, new Product { Name = "Corner flag" , Category = "Soccer" , Price = 34.95 M } } }; Func<Product, bool > categoryFilter = delegate (Product prod) { return prod.Category == "Soccer" ; }; decimal total = 0 ; foreach (Product prod in products.Filter(categoryFilter)) { total += prod.Price; } return View("Result" , (object )string .Format("Total: {0}" , total)); }
使用Lambda表达式代替委托定义(匿名函数)。
Func<Product, bool > categoryFilter = prod => prod.Category == "Soccer" ; ... foreach (Product prod in products.Filter(categoryFilter)){ total += prod.Price; }
也可以去掉Func,形成更紧凑的语法。
foreach (Product prod in products.Filter(prod => prod.Category == "Soccer" || prod.Price > 20 )){ total += prod.Price; }
自动类型接口 使用推断类型 或隐式类型 。
var myVariable = new Product{ Name = "Kayak" , Category = "Watersports" , Price = 275 M }; string name = myVariable.Name;
匿名类型 通过结合对象初始化器 和类型推断 ,可以创建简单的数据存储对象,而不需要定义相应的类 。
在HomeController添加CreateAnonArray 动作方法,创建匿名类型对象数组。
public ViewResult CreateAnonArray ( ) { var oddsAndEnds = new [] { new { Name = "MVC" , Category = "Pattern" }, new { Name = "Hat" , Category = "Clothing" }, new { Name = "Apple" , Category = "Fruit" } }; StringBuilder result = new StringBuilder(); foreach (var item in oddsAndEnds) { result.Append(item.Name).Append(" " ); } return View("Result" , (object )result.ToString()); }
语言集成查询(LINQ) 假设有一个Product对象的集合,需要找出其中的3个最高价,并将它们传递给View方法。
在HomeController添加FindProducts 动作方法。
public ViewResult FindProducts ( ) { Product[] products = { new Product { Name = "Kayak" , Category = "Watersports" , Price = 275 M }, new Product { Name = "Lifejacket" , Category = "Watersports" , Price = 48.95 M }, new Product { Name = "Soccer ball" , Category = "Soccer" , Price = 19.50 M }, new Product { Name = "Corner flag" , Category = "Soccer" , Price = 34.95 M } }; Product[] foundProducts = new Product[3 ]; Array.Sort(products, (item1, item2) => { return Comparer<decimal >.Default.Compare(item2.Price, item1.Price); }); Array.Copy(products, foundProducts, 3 ); StringBuilder result = new StringBuilder(); foreach (Product p in foundProducts) { result.AppendFormat("Price: {0} " , p.Price); } return View("Result" , (object )result.ToString()); }
使用LINQ的查询语法 。它对Product对象进行降序排序,并用select关键字返回一个匿名类型,而且仅包含Name和Price属性。
public ViewResult FindProducts ( ) { Product[] products = { new Product { Name = "Kayak" , Category = "Watersports" , Price = 275 M }, new Product { Name = "Lifejacket" , Category = "Watersports" , Price = 48.95 M }, new Product { Name = "Soccer ball" , Category = "Soccer" , Price = 19.50 M }, new Product { Name = "Corner flag" , Category = "Soccer" , Price = 34.95 M } }; var foundProducts = from match in products orderby match.Price descending select new { match.Name, match.Price }; int count = 0 ; StringBuilder result = new StringBuilder(); foreach (var p in foundProducts) { result.AppendFormat("Price: {0} " , p.Price); if (++count == 3 ) { break ; } } return View("Result" , (object )result.ToString()); }
也可以使用LINQ的点符号语法 。这种语法基于扩展方法 ,它运用一个IEnumerable<T>,并返回一个IEnumerable<T>,以方法链形式 调用,也称为链式语法 。
public ViewResult FindProducts ( ) { Product[] products = { new Product { Name = "Kayak" , Category = "Watersports" , Price = 275 M }, new Product { Name = "Lifejacket" , Category = "Watersports" , Price = 48.95 M }, new Product { Name = "Soccer ball" , Category = "Soccer" , Price = 19.50 M }, new Product { Name = "Corner flag" , Category = "Soccer" , Price = 34.95 M } }; var foundProducts = products.OrderByDescending(e => e.Price).Take(3 ).Select(e => new { e.Name, e.Price }); StringBuilder result = new StringBuilder(); foreach (var p in foundProducts) { result.AppendFormat("Price: {0} " , p.Price); } return View("Result" , (object )result.ToString()); }
OrderByDescending 方法重组了数据源中的数目,Take 方法返回结果最前面的指定数目的条目,Select 指定想要的结果。
一些有用的LINQ扩展方法
扩展方法
描述
延迟
All
如果源数据中的所有条目都与谓词匹配,则返回true
否
Any
如果源数据中至少有一个条目与谓词匹配,则返回true
否
Contains
如果数据源含有指定的条目或值,则返回true
否
Count
返回数据源中的条目数
否
First
返回数据源的第一个条目
否
FirstOrDefault
返回数据源的第一个条目,或无条目时,返回默认值
否
Last
返回数据源的最后一个条目
否
LastOrDefault
返回数据源的最后条目,或无条目时,返回默认值
否
Max Min
返回由lambda表达式表示的最大值或最小值
否
OrderBy OrderByDescending
基于lambda表达式返回的值对源数据进行排序
是
Reverse
反转数据源中数据项的顺序
是
Select
设计一个查询结果
是
SelectMany
把每个数据项投射到一个条目序列之中,然后把所有这些结果序列连接成一个序列
是
Single
返回数据源的第一个条目,或者有多个匹配时抛出一个异常
否
SingleOrDefault
返回数据源的第一个条目,或者无条目时,返回默认值;有多个匹配条目时,抛出一个异常
否
Skip SkipWhile
跳过指定数目的元素,或者当谓词匹配时跳过
是
Sum
对谓词选定的值求和
否
Take TakeWhile
从数据源的开始处选择指定数目的元素,或当谓词匹配时选择条目
是
ToArray ToDictionary ToList
把数据源转换成数组或其他集合类型
否
Where
过滤掉源数据中与谓词不匹配的条目
是
LINQ延迟查询 如果一条查询只含有延迟方法 ,那么,直到结果进行枚举后,才会执行该查询 。
... var foundProducts = products.OrderByDescending(e => e.Price).Take(3 ).Select(e => new { e.Name, e.Price });products[2 ] = new Product { Name = "Stadium" , Price = 79600 M }; ...
相比之下,使用任何非延迟的扩展方法 ,都会使LINQ查询立即执行 。
... var results = products.Sum(e => e.Price);products[2 ] = new Product { Name = "Stadium" , Price = 79600 M }; return View("Result" , (object )string .Format("Sum: {0:c}" , results));...
Async方法 异步方法 在后台执行和工作,并在工作完成时通知你,这允许在后台工作执行期间,你的代码能够处理其他事务。
异步方法是消除代码性能瓶颈 的一个重要工具,并让应用程序能够利用多处理器和处理器内核并行 地进行工作。
在Models文件夹添加MyAsyncMethods类,实现一个简单的异步方法。
public class MyAsyncMethods { public static Task<long ?> GetPageLength() { HttpClient client = new HttpClient(); var httpTask = client.GetAsync("http://apress.com" ); return httpTask.ContinueWith((Task<HttpResponseMessage> antecedent) => { return antecedent.Result.Content.Headers.ContentLength; }); } }
注意 :
当调用GetAsync方法时,所得到的是一个Task<HttpResponseMessage> 对象。
该请求将在后台执行 ,请求的结果是一个HttpResponseMessage 对象。
return 关键字用了两次。第一次是返回一个Task<HttpResponseMessage> 对象,第二次返回一个long? 。
ContinueWith 方法指定了后台任务完成时希望发生的事情。
运用async和await关键字 async 和await 关键字可以简化GetAsync异步方法 的使用。
public async static Task<long ?> GetPageLength(){ HttpClient client = new HttpClient(); var httpMessage = await client.GetAsync("http://apress.com" ); return httpMessage.Content.Headers.ContentLength; }
await:告诉C#编译器,希望等待GetAsync方法 所返回的Task结果 ,并继续执行同一方法中的其他语句。
async:使用await关键字时,必须在方法签名添加async关键字。