.net mvc笔记2_Essential C# Features
Essential C# Features
1、Using Automatically Implemented Properties
1 public class Product { 2 private string name; 3 4 public int ProductID { get; set; } 5 public string Name { //属性Name用常规方式实现 6 get { return ProductID + name;} 7 set { name = value; } 8 } 9 10 public string Description { get; set;} 11 public decimal Price { get; set; } 12 public string Category { set; get;} 13 }
2、Using Object and Collection Initializers
1 Listing 5-6. Constructing and Initializing an Object with Properties旧有做法对比 2 public class Product 3 { 4 public int ProductID { get; set; } 5 public string Name { get; set; } 6 public string Description { get; set; } 7 public decimal Price { get; set; } 8 public string Category { set; get; } 9 } 10 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 // create a new Product object 16 Product myProduct = new Product(); 17 // set the property values 18 myProduct.ProductID = 100; 19 myProduct.Name = "Kayak"; 20 myProduct.Description = "A boat for one person"; 21 myProduct.Price = 275M; 22 myProduct.Category = "Watersports"; 23 // process the product 24 ProcessProduct(myProduct); 25 } 26 private static void ProcessProduct(Product prodParam) 27 { 28 //...statements to process product in some way 29 } 30 }
1 Listing 5-7. Using the Object Initializer Feature 2 public class Product 3 { 4 public int ProductID { get; set; } 5 public string Name { get; set; } 6 public string Description { get; set; } 7 public decimal Price { get; set; } 8 public string Category { set; get; } 9 } 10 11 class Program 12 { 13 static void Main(string[] args) 14 { 15 // create a new Product object 16 ProcessProduct(new Product 17 { 18 ProductID = 100, 19 Name = "Kayak", 20 Description = "A boat for one person", 21 Price = 275M, 22 Category = "Watersports" 23 }); 24 } 25 private static void ProcessProduct(Product prodParam) 26 { 27 //...statements to process product in some way 28 } 29 }
1 Listing 5-8. Initializing Collections and Arrays 2 using System.Collections.Generic; 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 string[] stringArray = { "apple", "orange", "plum" }; 8 List<int> intList = new List<int> { 10, 20, 30, 40 }; 9 Dictionary<string, int> myDict = new Dictionary<string, int> { 10 { "apple", 10 }, 11 { "orange", 20 }, 12 { "plum", 30 } 13 }; 14 } 15 }
3、Using Extension Methods
扩展方法,假设已有类ShoppingCart,在不修改这个类本身的情况下,如果要为它增加method,就可以使用扩展方法。扩展方法并不能打破ShoppingCart的封装性和对数据的访问规则,仍然是只能访问允许访问的数据,如public。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace _5_11ExtensionMethod 7 { 8 public class Product 9 { 10 public int ProductID { get; set; } 11 public string Name { get; set; } 12 public string Description { get; set; } 13 public decimal Price { get; set; } 14 public string Category { set; get; } 15 16 } 17 18 public class ShoppingCart 19 { 20 public List<Product> Products { get; set; } 21 } 22 23 public static class MyExtensionMethods 24 { 25 public static decimal TotalPrices(this ShoppingCart cartParam) 26 { 27 decimal total = 0; 28 foreach (Product prod in cartParam.Products) 29 { 30 total += prod.Price; 31 } 32 return total; 33 } 34 } 35 36 37 class Program 38 { 39 static void Main(string[] args) 40 { 41 ShoppingCart cart = new ShoppingCart 42 { 43 Products = new List<Product> { 44 new Product {Name = "Kayak", Price = 275M}, 45 new Product {Name = "Lifejacket", Price = 48.95M}, 46 new Product {Name = "Soccer ball", Price = 19.50M}, 47 new Product {Name = "Corner flag", Price = 34.95M} 48 } 49 }; 50 decimal cartTotal = cart.TotalPrices(); 51 Console.WriteLine("Total: {0:c}", cartTotal); 52 } 53 } 54 }
4、Applying an Extension Method to Different Implementations of the Same Interface
将扩展方法应用到接口上
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; 6 7 namespace ExtensionMethodInterface 8 { 9 public class Product 10 { 11 public int ProductID { get; set; } 12 public string Name { get; set; } 13 public string Description { get; set; } 14 public decimal Price { get; set; } 15 public string Category { set; get; } 16 } 17 18 public class ShoppingCart : IEnumerable<Product> 19 { 20 public List<Product> Products { get; set; } 21 22 public IEnumerator<Product> GetEnumerator() 23 { 24 return Products.GetEnumerator(); 25 } 26 27 IEnumerator IEnumerable.GetEnumerator() 28 { 29 return GetEnumerator(); 30 } 31 } 32 33 public static class MyExtensionMethods 34 { 35 public static decimal TotalPrices(this IEnumerable<Product> productEnum) 36 { 37 decimal total = 0; 38 foreach (Product prod in productEnum) 39 { 40 total += prod.Price; 41 } 42 return total; 43 } 44 } 45 46 class Program 47 { 48 static void Main(string[] args) 49 { 50 // create and populate ShoppingCart 51 IEnumerable<Product> products = new ShoppingCart 52 { 53 Products = new List<Product> 54 { 55 new Product {Name = "Kayak", Price = 275M}, 56 new Product {Name = "Lifejacket", Price = 48.95M}, 57 new Product {Name = "Soccer ball", Price = 19.50M}, 58 new Product {Name = "Corner flag", Price = 34.95M} 59 } 60 }; 61 62 // create and populate an array of Product objects 63 Product[] productArray = 64 { 65 new Product {Name = "Kayak", Price = 275M}, 66 new Product {Name = "Lifejacket", Price = 48.95M}, 67 new Product {Name = "Soccer ball", Price = 19.50M}, 68 new Product {Name = "Corner flag", Price = 34.95M} 69 }; 70 71 // get the total value of the products in the cart 72 decimal cartTotal = products.TotalPrices(); 73 decimal arrayTotal = productArray.TotalPrices(); 74 Console.WriteLine("Cart Total: {0:c}", cartTotal); 75 Console.WriteLine("Array Total: {0:c}", arrayTotal); 76 } 77 } 78 }
5、Using the Filtering Extension Method
使用带过滤器的扩展方法
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; //添加 6 7 namespace _5_15FilterExtensionMethod 8 { 9 public class Product 10 { 11 public int ProductID { get; set; } 12 public string Name { get; set; } 13 public string Description { get; set; } 14 public decimal Price { get; set; } 15 public string Category { set; get; } 16 } 17 18 public class ShoppingCart : IEnumerable<Product> 19 { 20 public List<Product> Products { get; set; } 21 22 public IEnumerator<Product> GetEnumerator() 23 { 24 return Products.GetEnumerator(); 25 } 26 27 IEnumerator IEnumerable.GetEnumerator() 28 { 29 return GetEnumerator(); 30 } 31 } 32 33 public static class MyExtensionMethods //扩展方法类 34 { 35 public static IEnumerable<Product> FilterByCategory( 36 this IEnumerable<Product> productEnum, string categoryParam) //带过滤器的扩展方法 37 { 38 foreach (Product prod in productEnum) 39 { 40 if (prod.Category == categoryParam) 41 { 42 yield return prod; 43 } 44 } 45 } 46 } 47 48 class Program 49 { 50 static void Main(string[] args) 51 { 52 // create and populate ShoppingCart 53 IEnumerable<Product> products = new ShoppingCart 54 { 55 Products = new List<Product> 56 { 57 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 58 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 59 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 60 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 61 } 62 }; 63 foreach (Product prod in products.FilterByCategory("Soccer")) 64 { 65 Console.WriteLine("Name: {0}, Price {1:c}", prod.Name, prod.Price); 66 } 67 } 68 } 69 }
显示结果为:
Name: Soccer ball, Price $19.50
Name: Corner flag, Price $34.95
6、Using Lambda Expressions
(1)定义委托函数的方式
在扩展方法中使用委托来完成条件过滤。然后对每个要过滤的条件形成一个委托函数。要使用哪一个条件,就将该条件对应的委托函数作为参数装配到扩展方法上。
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; //添加 6 7 namespace _5_19DelegateFunc 8 { 9 public class Product 10 { 11 public int ProductID { get; set; } 12 public string Name { get; set; } 13 public string Description { get; set; } 14 public decimal Price { get; set; } 15 public string Category { set; get; } 16 } 17 18 public class ShoppingCart : IEnumerable<Product> 19 { 20 public List<Product> Products { get; set; } 21 22 public IEnumerator<Product> GetEnumerator() 23 { 24 return Products.GetEnumerator(); 25 } 26 27 IEnumerator IEnumerable.GetEnumerator() //IEnumerator需要using System.Collections; 28 { 29 return GetEnumerator(); 30 } 31 } 32 33 public static class MyExtensionMethods //扩展方法类 34 { 35 public static IEnumerable<Product> Filter( //第一个参数前有this关键字是扩展方法,注意要放在扩展方法类当中 36 this IEnumerable<Product> productEnum, //第二个参数接收委托 37 Func<Product, bool> selectorParam ) 38 { 39 foreach (Product prod in productEnum) 40 { 41 if (selectorParam(prod)) 42 { 43 yield return prod; 44 } 45 } 46 } 47 } 48 49 class Program 50 { 51 static void Main(string[] args) 52 { 53 // create and populate ShoppingCart 54 IEnumerable<Product> products = new ShoppingCart 55 { 56 Products = new List<Product> 57 { 58 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 59 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 60 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 61 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 62 } 63 }; 64 65 Func<Product, bool> categoryFilter = delegate(Product prod) //定义委托函数 66 { 67 return prod.Category == "Soccer"; 68 }; 69 70 //将委托函数装配到扩展方法Filter上,得到过滤后的结果 71 IEnumerable<Product> filteredProducts = products.Filter(categoryFilter); 72 73 foreach (Product prod in filteredProducts) 74 { 75 Console.WriteLine("Name: {0}, Price: {1:c}", prod.Name, prod.Price); 76 } 77 78 } 79 } 80 }
在上面这种情形下,我们可以用委托中指定的任何条件来过滤Product对象了,但我们必须为我们希望的每个条件定义一个Func,这是不理想的。替代方法是使用lambda表达式,它以一种简洁的格式在委托中表达一个方法的实现部分。我们可以用它来替换我们的委托定义
(2)lambda表达式
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; 6 namespace _5_22LambdaExpression 7 { 8 public class Product 9 { 10 public int ProductID { get; set; } 11 public string Name { get; set; } 12 public string Description { get; set; } 13 public decimal Price { get; set; } 14 public string Category { set; get; } 15 } 16 17 public class ShoppingCart : IEnumerable<Product> 18 { 19 public List<Product> Products { get; set; } 20 21 public IEnumerator<Product> GetEnumerator() 22 { 23 return Products.GetEnumerator(); 24 } 25 26 IEnumerator IEnumerable.GetEnumerator() //IEnumerator需要using System.Collections; 27 { 28 return GetEnumerator(); 29 } 30 } 31 32 public static class MyExtensionMethods //扩展方法类 33 { 34 public static IEnumerable<Product> Filter( //第一个参数前有this关键字是扩展方法,注意要放在扩展方法类当中 35 this IEnumerable<Product> productEnum, //第二个参数接收委托 36 Func<Product, bool> selectorParam) 37 { 38 foreach (Product prod in productEnum) 39 { 40 if (selectorParam(prod)) 41 { 42 yield return prod; 43 } 44 } 45 } 46 } 47 48 class Program 49 { 50 static void Main(string[] args) 51 { 52 // create and populate ShoppingCart 53 IEnumerable<Product> products = new ShoppingCart 54 { 55 Products = new List<Product> 56 { 57 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 58 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 59 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 60 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 61 } 62 }; 63 64 //用Lambda表达式代替委托函数的函数体 65 Func<Product, bool> categoryFilter = prod => prod.Category == "Soccer"; 66 IEnumerable<Product> filteredProducts = products.Filter(categoryFilter); 67 //可以完全省略掉Func,将上面两句可以写成更紧凑的语法如下 68 //IEnumerable<Product> filteredProducts = products.Filter(prod =>
// prod.Category == "Soccer"); 69 70 foreach (Product prod in filteredProducts) 71 { 72 Console.WriteLine("Name: {0}, Price: {1:c}", prod.Name, prod.Price); 73 } 74 75 } 76 } 77 }
我们可以通过扩展lambda表达式组合多个过滤条件
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; 6 7 namespace _5_22LambdaExpression 8 { 9 public class Product 10 { 11 public int ProductID { get; set; } 12 public string Name { get; set; } 13 public string Description { get; set; } 14 public decimal Price { get; set; } 15 public string Category { set; get; } 16 } 17 18 public class ShoppingCart : IEnumerable<Product> 19 { 20 public List<Product> Products { get; set; } 21 22 public IEnumerator<Product> GetEnumerator() 23 { 24 return Products.GetEnumerator(); 25 } 26 27 IEnumerator IEnumerable.GetEnumerator() //IEnumerator需要using System.Collections; 28 { 29 return GetEnumerator(); 30 } 31 } 32 33 public static class MyExtensionMethods //扩展方法类 34 { 35 public static IEnumerable<Product> Filter( //第一个参数前有this关键字是扩展方法,注意要放在扩展方法类当中 36 this IEnumerable<Product> productEnum, //第二个参数接收委托 37 Func<Product, bool> selectorParam) 38 { 39 foreach (Product prod in productEnum) 40 { 41 if (selectorParam(prod)) 42 { 43 yield return prod; 44 } 45 } 46 } 47 } 48 49 class Program 50 { 51 static void Main(string[] args) 52 { 53 // create and populate ShoppingCart 54 IEnumerable<Product> products = new ShoppingCart 55 { 56 Products = new List<Product> 57 { 58 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 59 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 60 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 61 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 62 } 63 }; 64 65 66 //组合多个条件 67 IEnumerable<Product> filteredProducts = products.Filter(prod => 68 prod.Category == "Soccer" || prod.Price > 20); 69 70 foreach (Product prod in filteredProducts) 71 { 72 Console.WriteLine("Name: {0}, Price: {1:c}", prod.Name, prod.Price); 73 } 74 75 } 76 } 77 }
显示结果为:
Name: Kayak, Price $275.00
Name: Lifejacket, Price $48.95
Name: Soccer ball, Price $19.50
Name: Corner flag, Price $34.95
7、 OTHER FORMS FOR LAMBDA EXPRESSIONS
我们可以不必在Lambda表达式中直接表现委托逻辑。可以用调用另一个方法来实现,如下所示:
prod => EvaluateProduct(prod)
另外,如果我们需要用Lambda表达式来表示一个多参数的委托,我们可以把参数封装在在括号内,如下:
(prod, count) => prod.Price > 20 && count > 0
最后,如果Lambda表达式中的逻辑需要多条语句,我们可以用花括号括起来,并用一条返回语句来完成,如下:
(prod, count) =>
{
//...multiple code statements
return result;
}
8、Using Automatic Type Inference
Listing 5-23. Using Type Inference
1 var myVariable = new Product { Name = "Kayak", Category = "Watersports", Price = 275M }; 2 string name = myVariable.Name; // legal 3 int count = myVariable.Count; // compiler error
C#的var关键字允许你定义一个局部变量而不给它指定确切类型,这叫做类型推断(type inference),或者隐式类型推导(implicit typing)。(上面例子中的myVariable在定义时就没有明确指定类型,只是用var来定义)并不是myVariable没有类型,只是我们要求编译器从代码中来推断出它。可以在语句中看到,编译器只允许推断类(这里是Product)中的成员被调用。(所以myVariable.Name合法,而myVariable.Cout就会引起编译错误。)
9、Using Anonymous Types
通过结合对象初始化器(object initializers)和类型推断,我们可以创建一个简单数据存储对象(data-storage objects),而不需要定义相应的类或结构。(也就是说不事先定义类或结构,直接用new就生成对象。)
Listing 5-24. Creating an Anonymous Type
1 var myAnonType = new { 2 Name = "MVC", 3 Category = "Pattern" 4 }; 5 Console.WriteLine("Name: {0}, Type: {1}", myAnonType.Name, myAnonType.Category);
在这个例子中,myAnonType是一个匿名类型对象。这并不意味着它是动态的,JavaScript变量在这种情况下才是动态类型的。这只表示类型定义由编译器自动生成。强类型仍然是必须的。例如,你只可以获取和设置初始化器中已经定义的那些属性。
C#编译器基于初始化器中参数的名字和类型来生成这个类。有同样属性名和类型的两个匿名类型对象将被指派为自动生成的同样的类。意即,我们可以生成匿名类型对象的数组。
Listing 5-25. Creating an Array of Anonymously Typed Objects
1 var oddsAndEnds = new[] { 2 new { Name = "MVC", Category = "Pattern"}, 3 new { Name = "Hat", Category = "Clothing"}, 4 new { Name = "Apple", Category = "Fruit"} 5 }; 6 foreach (var item in oddsAndEnds) { 7 Console.WriteLine("Name: {0}", item.Name); 8 }
10、Performing Language Integrated Queries
Language Integrated Query简称LINQ。到目前为止,我们所描述的所有语言特性都可以很好地用在LINQ中。我们热爱LINQ。它是一种奇妙而奇怪的强制添加到.NET的功能。如果你还从没用过LINQ,你就已经出局了。LINQ通过类SQL的语法(SQL-like syntax)来在类中查询数据。假设我们有一个Product对象的集合,我们想找出价格最高的三个,并打印出名字和价格。没有LINQ,我们需要如表5-26所示来完成。
Listing 5-26. Querying Without LINQ
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 7 namespace _5_26WithoutLINQ 8 { 9 public class Product 10 { 11 public int ProductID { get; set; } 12 public string Name { get; set; } 13 public string Description { get; set; } 14 public decimal Price { get; set; } 15 public string Category { set; get; } 16 } 17 18 class Program 19 { 20 static void Main(string[] args) 21 { 22 Product[] products = { 23 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 24 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 25 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 26 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 27 }; 28 29 // define the array to hold the results 30 Product[] results = new Product[3]; 31 32 // 对数组中元素排序,item1和item2参数位置在Sort和Compare中一致时表示升序 33 //Array.Sort(products, (item1, item2) => 34 //{ 35 // return Comparer<decimal>.Default.Compare(item1.Price, item2.Price); 36 //}); 37 38 //item1和item2参数位置在Sort和Compare中相反时表示降序 39 //排序后数组元素中前三个为价格最高的3个产品 40 Array.Sort(products, (item1, item2) => 41 { 42 return Comparer<decimal>.Default.Compare(item2.Price, item1.Price); 43 }); 44 45 // get the first three items in the array as the results 46 //把数组products前3个元素拷贝到数组results中 47 Array.Copy(products, results, 3); 48 // print out the names 49 foreach (Product p in results) 50 { 51 Console.WriteLine("Item: {0}, Cost: {1}", p.Name, p.Price); 52 } 53 } 54 } 55 }
显示结果为:
Item: kayak, Cost:275
Item: Lifejacket, Cost:48.95
Item: Corner flag, Cost:34.95
下面我们用LINQ来实现。
Listing 5-27. Using LINQ to Query Data
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace _5_27UsingLINQ 7 { 8 public class Product 9 { 10 public int ProductID { get; set; } 11 public string Name { get; set; } 12 public string Description { get; set; } 13 public decimal Price { get; set; } 14 public string Category { set; get; } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 Product[] products = { 22 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 23 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 24 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 25 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 26 }; 27 28 var results = from product in products 29 orderby product.Price descending 30 select new { 31 product.Name, 32 product.Price 33 }; 34 int count = 0; 35 // print out the names 36 foreach (var p in results) 37 { 38 Console.WriteLine("Item: {0}, Cost: {1}", p.Name, p.Price); 39 if (++count == 3) 40 { 41 break; 42 } 43 } 44 } 45 } 46 }
这要简洁得多,可以看到类SQL的查询。我们以降序对Product对象进行排序,并用关键字select返回只包含我们想要属性的匿名类型。LINQ的这种风格称为查询语法,是大多数开发人员所熟悉的。本例中这个查询的缺点是它将数组中的每个Product都返回到了一个匿名类型对象中。所以我们需要处理结果,以获得最开始的三个,并打印出来。
然而,如果我们愿意放弃上面这种查询语法的简单性,我们可以得到功能更强大的LINQ。可选用的方法是基于扩展方法(extension methods)的点号表示法语法,或者叫点号表示法。表5-28演示了这中科选方式来处理我们的Product对象。
Listing 5-28. Using LINQ Dot Notation
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace _5_28DotNotation 7 { 8 public class Product 9 { 10 public int ProductID { get; set; } 11 public string Name { get; set; } 12 public string Description { get; set; } 13 public decimal Price { get; set; } 14 public string Category { set; get; } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 Product[] products = { 22 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 23 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 24 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 25 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 26 }; 27 28 var results = products 29 .OrderByDescending(e => e.Price) 30 .Take(3) 31 .Select(e => new { e.Name, e.Price }); 32 foreach (var p in results) 33 { 34 Console.WriteLine("Item: {0}, Cost: {1}", p.Name, p.Price); 35 } 36 } 37 } 38 }
OrderByDescending方法重组数据源中的条目。这里,lambda表达式返回我们要用来比较的值。Take方法返回结果最前面(这是我们用查询语法不能实现的)的一个指定数目的条目。Select方法允许我们设计结果,指定我们想要的结果。这里,我们设计一个匿名对象,它包含了Name和Price属性。注意,我们甚至不需要指定匿名类型中的属性名。C#已经根据我们在Select方法中拾取的属性推断了这些名字。
表5-1描述了最有用的LINQ扩展方法。本书中我们到处都使用LINQ,当你看到一个以前还未遇见的扩展方法时,回过来查看此表可能是很有用的。表5-1所显示的所有LINQ方法都是在IEnumerable<T>上进行操作。
Table 5-1. Some Useful LINQ Extension Methods
Extension Method |
Description |
Deferred |
First |
Returns the
first item from the data source |
No |
FirstOrDefault |
Returns the
first item from the data source or the default value if there are no items |
No |
Last |
Returns the
last item in the data source |
No |
LastOrDefault |
Returns the
last item in the data source or the default value if there are no items |
No |
Max Min |
Returns the
largest or smallest value specified by a lambda expression |
No |
OrderBy OrderByDescending |
Sorts the
source data based on the value returned by the lambda expression |
Yes |
Reverse |
Reverses the
order of the items in the data source |
Yes |
Select |
Projects a
result from a query |
Yes |
SelectMany |
Projects each
data item into a sequence of items and then concatenates all of those
resulting sequences into a single sequence |
Yes |
Single |
Returns the
first item from the data source or throws an exception if there are multiple
matches |
No |
SingleOrDefault |
Returns the
first item from the data source or the default value if there are no items,
or throws an exception if there are multiple matches |
No |
Skip SkipWhile |
Skips over a
specified number of elements, or skips while the predicate matches |
Yes |
Sum |
Totals the
values selected by the predicate |
No |
Take TakeWhile |
Selects a
specified number of elements from the start of the data source or selects
items while the predicate matches |
Yes |
ToArray ToDictionary ToList |
Converts the
data source to an array or other collection type |
No |
Where |
Filters items
from the data source that do not match the predicate |
Yes |
11、Understanding Deferred LINQ Queries
你可能注意到表5-1中最后一列是”延迟”(Deferred)。扩展方法在LINQ查询中执行时有一个有趣的变异。含有deferred方法的查询要直到IEnumerable<T>结果中的条目被枚举时才会执行,如列表5-29所示
Listing 5-29. Using Deferred LINQ Extension Methods in a Query
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace _5_29 DeferredLINQ 7 { 8 public class Product 9 { 10 public int ProductID { get; set; } 11 public string Name { get; set; } 12 public string Description { get; set; } 13 public decimal Price { get; set; } 14 public string Category { set; get; } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 Product[] products = { 22 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 23 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 24 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 25 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 26 }; 27 28 var results = products 29 .OrderByDescending(e => e.Price) 30 .Take(3) 31 .Select(e => new { e.Name, e.Price }); 32 products[2] = new Product{ Name=”Stadium”, Price=79500M }; 33 foreach (var p in results) 34 { 35 Console.WriteLine("Item: {0}, Cost: {1}", p.Name, p.Price); 36 } 37 } 38 } 39 }
OrderByDescending扩展方法是延迟的,也就是虽然定义好了查询,但是要等到枚举的时候才执行。所以本例中,通过products[2] = new Product{ Name=”Stadium”, Price=79500M };来修改了product[2],最后枚举的时候才执行查询,结果为:
Item: Stadium, Cost: 79500
Item: Kayak, Cost: 275
Item: Lifejacket, Cost: 48.95
你可以看到,直到结果被枚举时,才会评估这个查询,因此,我们所做的修改 — 把Stadium引入到Product数组 — 会反映在输出中。相比之下,用任何无deferred扩展方法都会使LINQ查询立即执行。列表5-30提供了一个演示。
Listing 5-30. An Immediately Executed LINQ Query
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace _5_30 ImmediatelyExecutedLINQ 7 { 8 public class Product 9 { 10 public int ProductID { get; set; } 11 public string Name { get; set; } 12 public string Description { get; set; } 13 public decimal Price { get; set; } 14 public string Category { set; get; } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 Product[] products = { 22 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 23 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 24 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 25 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 26 }; 27 28 var results = products.Sum(e => e.Price); //立即执行 29 30 products[2] = new Product{ Name=”Stadium”, Price=79500M }; 31 foreach (var p in results) 32 { 33 Console.WriteLine("Item: {0}, Cost: {1}", p.Name, p.Price); 34 } 35 } 36 } 37 }
因为立即执行,所以后面的修改就影响不到最后的结果了。结果为:
Sum: $378.40
12、Repeatedly Using a Deferred Query
有延迟的LINQ扩展方法引起的一个有趣特性是每次枚举结果时都是从头开始。
Listing 5-31. Repeatedly Executing a Deferred Query
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace _5_31 RepeatedlyDeferredLINQ 7 { 8 public class Product 9 { 10 public int ProductID { get; set; } 11 public string Name { get; set; } 12 public string Description { get; set; } 13 public decimal Price { get; set; } 14 public string Category { set; get; } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 Product[] products = { 22 new Product {Name = "Kayak", Category = "Watersports", Price = 275M}, 23 new Product {Name = "Lifejacket", Category = "Watersports", Price = 48.95M}, 24 new Product {Name = "Soccer ball", Category = "Soccer", Price = 19.50M}, 25 new Product {Name = "Corner flag", Category = "Soccer", Price = 34.95M} 26 }; 27 28 var results = products 29 .OrderByDescending(e => e.Price) 30 .Take(3) 31 .Select(e => new { e.Name, e.Price }); 32 foreach (var p in results) 33 { 34 Console.WriteLine("Item: {0}, Cost: {1}", p.Name, p.Price); 35 } 36 37 Console.WriteLine("---End of results---"); 38 products[2] = new Product{ Name=”Stadium”, Price=79500M }; 39 foreach (var p in results) 40 { 41 Console.WriteLine("Item: {0}, Cost: {1}", p.Name, p.Price); 42 } 43 } 44 } 45 }
这个例子创建了数据,定义了具有延迟的LINQ查询,然后枚举查询结果。之后,其中一个数组元素被修改,再重新枚举查询结果。显示结果为:
Item: Kayak, Cost: 275
Item: Lifejacket, Cost: 48.95
Item: Corner flag, Cost: 34.95
---End of results---
Item: Stadium, Cost: 79500
Item: Kayak, Cost: 275
Item: Lifejacket, Cost: 48.95
---End of results--