///<summary>/// 定义一个静态类,用于实现扩展方法///</summary>publicstaticclass MyExtensionMethods {
///<summary>
/// 商品查询器
///</summary>
///<param name="productEnum">扩展类型的实例引用</param>
///<param name="selectorParam">一个参数类型为Product,返回值为bool的委托</param>
///<returns>查询结果</returns>
public static IEnumerable<Product> Filter(this IEnumerable<Product> productEnum, Func<Product bool> selectorParam) {
foreach (Product prod in productEnum) {
if (selectorParam(prod)) {
yieldreturn prod;
}
}
}
}
没错,我们就是用这么简短的Filter方法来满足各种需求的查询。上面Product类使用的是前文定义的。这里也再一次见证了扩展方法的功效。为了演示Filter查询方法的调用,我们先来造一批数据:
staticvoid Main(string[] args) {
// 创建商品集合
IEnumerable<Product> products = new ShoppingCart {
Products = new List<Product> {
new Product {Name = "西瓜", Category = "水果", Price = 2.3M},
new Product {Name = "苹果", Category = "水果", Price = 4.9M},
new Product {Name = "ASP.NET MCV 入门", Category = "书籍", Price = 19.5M},
new Product {Name = "ASP.NET MCV 提高", Category = "书籍", Price = 34.9M}
}
};
}
接下来我们继续在上面Main方法中来调用查询方法Filter:
//用匿名函数定义一个具体的查询需求
Func<Product, bool> fruitFilter = delegate(Product prod) {
return prod.Category == "水果";
};
//调用Filter,查询分类为“水果”的商品
IEnumerable<Product> filteredProducts = products.Filter(fruitFilter);
//打印结果foreach (Product prod in filteredProducts) {
Console.WriteLine("商品名称: {0}, 单价: {1:c}", prod.Name, prod.Price);
}
Console.ReadKey();
上面我们使用的是委托和匿名函数来处理用户查询逻辑,并把它传递给Filter方法,满足了前面所说的需求。但若使用Lambda表达式代替上面的匿名函数能使上面的代码看上去更简洁更人性化,如下代码所示:
Func<Product, bool> fruitFilter = prod => prod.Category == "水果";
IEnumerable<Product> filteredProducts = products.Filter(fruitFilter);
没有了delegate关键字,没有了大小括号,看上去更舒服。当然上面两行代码可以继续简化为一行:
IEnumerable<Product> filteredProducts = products.Filter(prod => prod.Category == "水果");
这三种方式输出结果都是一样的。然后,我们还可以通过Lambda表达式实现各种需求的查询:
//查询分类为“水果”或者单价大于30元的商品
IEnumerable<Product> filteredProducts = products.Filter(prod =>
prod.Category == "水果" || prod.Price >30
);
6.LINQ
最后简单回顾一下LINQ。LINQ(Language Integrated Query语言集成查询)是 VS 2008 和 .NET Framework 3.5 版中一项突破性的创新,它在对象领域和数据领域之间架起了一座桥梁。
上面讲Lambda表达式时,用到的查询结果集的方式未免还是有点麻烦(因为自定义了一个Filter扩展方法),而Linq本身就集合了很多扩展方法,我们可以直接使用,大大的简化了编写查询代码的工作。例如,对于这样一个数据集合:
Product[] products = {
new Product {Name = "西瓜", Category = "水果", Price = 2.3M},
new Product {Name = "苹果", Category = "水果", Price = 4.9M},
new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M},
new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M}
};
如果要查询得到价钱最高的三个商品信息,如果不使用Linq,我们可能会先写一个排序方法,对products根据价钱由高到低进行排序,排序时需要创建一个新的Product[]对象用于存储排序好的数据。但用Linq可大大减少工作量,一两句代码就能搞定。如下代码所示,查出价钱最高的三个商品:
var results = from product in products
orderby product.Price descending
selectnew {
product.Name,
product.Price
};//打印价钱最高的三个商品
int count = 0;
foreach (var p in results) {
Console.WriteLine("商品:{0},价钱:{1}", p.Name, p.Price);
if (++count == 3) break;
}
Console.ReadKey();
能熟练使用Linq是一件很爽的事情。上面的Linq语句和我们熟悉的SQL查询语句类似,看上去非常整洁且易懂。但并不是每一种SQL查询语句在C#都有对应的关键字,有时候我们需要使用另外一种Linq查询方式,即“点号”方式的Linq查询方式,这种方式中的Linq查询方法都是扩展方法。如下面这段代码和上面实现的效果是一样的:
var results = products
.OrderByDescending(e => e.Price)
.Take(3)
.Select(e =>new { e.Name,e.Price});
foreach (var p in results) {
Console.WriteLine("商品:{0},价钱:{1}", p.Name, p.Price);
}
Console.ReadKey();
虽然类SQL的Linq查询方式比这种方式看上去更一目了然,但并不是每一种SQL查询语句在C#都有对应的关键字,比如这里的Take扩展方法就是类SQL的Linq查询语法没有的功能。
注意,有些Linq扩展方法分为“延后查询”(deferred)和“即时查询”(immediate)。延后查询意思是拥有“延后查询”扩展方法的Linq语句只有当调用结果集对象的时候才开始真正执行查询,即时查询则是立即得到结果。比如上面的Linq语句的OrderByDescending扩展方法就是一个“延后查询”方法,当程序执行到Linq语句定义部分时并没有查询出结果并放到results对象中,而是当程序执行到foreach循环时才真正执行Linq查询语句得到查询结果。我们可以做个测试,在Ling语句之后,我们再将products[1]对象重新赋值,如下代码所示:
var results = products
.OrderByDescending(e => e.Price)
.Take(3)
.Select(e =>new { e.Name, e.Price });
//在Linq语句之后对products[1]重新赋值
products[1] = new Product { Name = "榴莲", Category = "水果", Price = 22.6M };
//打印foreach (var p in results) {
Console.WriteLine("商品:{0},价钱:{1}", p.Name, p.Price);
}
Console.ReadKey();