在C#中, 适当地使用Lambda表达式, 可以让我们的代码更优雅.
通过lambda表达式, 我们可以很方便地创建一个delegate:
下面两个语句是等价的
// using delegate syntax
Func < int , int > f = delegate ( int i) { return ++ i; };
// using lambda syntax
Func < int , int > f = i => ++ i;
但我更喜欢后者!
假如我要对一个列表进行排序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main( string [] args)
{
List < Book > books = new List < Book >
{
new Book
{
Id = 1 ,
Title = " Design Patterns " ,
Authors = new List < string > { " Erich Gamma " , " Richard Helm " , " Ralph Johnson " , " John Vlissides " }
},
new Book
{
Id = 2 ,
Title = " Refactoring " ,
Authors = new List < string > { " Martin Fowler " }
}
};
books.Sort((x, y) => x.Authors.Count - y.Authors.Count); // 以作者个数进行排序
}
}
public class Book
{
public int Id { get ; set ; }
public string Title { get ; set ; }
public List < string > Authors { get ; set ; }
}
}
的确很方便吧!
我猜想List<T>应该提供了这样的方法:
1 // 判断是否存在Id==1的书
2 bool has_book_with_id_equals_one = books.Contains( new Book { Id = 1 }, (x, y) => x.Id == y.Id);
3
4 // 判断是否存在Title=="REFACTORING"(不区分大小写)的书
5 bool has_book_with_title_equals_refactoring_ignorecase = books.Contains( new Book { Title = " REFACTORING " }, x => x.Title.ToUpper());
不幸的是, List<T>没有这样的方法。
按照传统的方法, 你可能会这样写:
1 // 新增一个实现了IEqualityComparer<Book>接口的类
2 public class BookComparer : IEqualityComparer < Book >
3 {
4 public bool Equals(Book x, Book y)
5 {
6 return x.Id == y.Id;
7 }
8 public int GetHashCode(Book obj)
9 {
10 return obj.Id.GetHashCode();
11 }
12 }
13 // 然后再干我们的活
14 bool has_book_with_id_equals_one = books.Contains( new Book { Id = 1 }, new BookComparer());
很无奈的选择, 但是没办法!
幸运的是,我们可以自己动手扩展List<T>接口:
1 public class KeyEqualityComparer < T > : IEqualityComparer < T >
2 {
3 private readonly Func < T, T, bool > comparer;
4 private readonly Func < T, object > keyExtractor;
5
6 // Allows us to simply specify the key to compare with: y => y.ID
7 public KeyEqualityComparer(Func < T, object > keyExtractor) : this (keyExtractor, null ) { }
8
9 // Allows us to tell if two objects are equal: (x, y) => y.Id == x.Id
10 public KeyEqualityComparer(Func < T, T, bool > comparer) : this ( null , comparer) { }
11
12 public KeyEqualityComparer(Func < T, object > keyExtractor, Func < T, T, bool > comparer)
13 {
14 this .keyExtractor = keyExtractor;
15 this .comparer = comparer;
16 }
17
18 public bool Equals(T x, T y)
19 {
20 if (comparer != null )
21 return comparer(x, y);
22 else
23 {
24 var valX = keyExtractor(x);
25 if (valX is IEnumerable < object > ) // The special case where we pass a list of keys
26 return ((IEnumerable < object > )valX).SequenceEqual((IEnumerable < object > )keyExtractor(y));
27 return valX.Equals(keyExtractor(y));
28 }
29 }
30
31 public int GetHashCode(T obj)
32 {
33 if (keyExtractor == null )
34 return obj.ToString().ToLower().GetHashCode();
35 else
36 {
37 var val = keyExtractor(obj);
38 if (val is IEnumerable < object > ) // The special case where we pass a list of keys
39 return ( int )((IEnumerable < object > )val).Aggregate((x, y) => x.GetHashCode() ^ y.GetHashCode());
40 return val.GetHashCode();
41 }
42 }
43 }
44
45 public static class MyExtMethod
46 {
47 public static bool Contains < T > ( this IEnumerable < T > list, T item, Func < T, object > keyExtractor)
48 {
49 return list.Contains(item, new KeyEqualityComparer < T > (keyExtractor));
50 }
51
52 public static bool Contains < T > ( this IEnumerable < T > list, T item, Func < T, T, bool > comparer)
53 {
54 return list.Contains(item, new KeyEqualityComparer < T > (comparer));
55 }
56 }
注意到上面的代码不仅仅针对Book,而是任意类型T
现在,我们就可以无拘无束地用我们的自己的扩展方法了(完整代码):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main( string [] args)
{
List < Book > books = new List < Book >
{
new Book
{
Id = 1 ,
Title = " Design Patterns " ,
Authors = new List < string > { " Erich Gamma " , " Richard Helm " , " Ralph Johnson " , " John Vlissides " }
},
new Book
{
Id = 2 ,
Title = " Refactoring " ,
Authors = new List < string > { " Martin Fowler " }
}
};
books.Sort((x, y) => x.Authors.Count - y.Authors.Count);
bool has_book_with_id_equals_one = books.Contains(new Book { Id = 1 }, (x, y) => x.Id == y.Id);
bool has_book_with_title_equals_refactoring_ignorecase = books.Contains(new Book { Title = "REFACTORING" }, x => x.Title.ToUpper());
}
}
public class Book
{
public int Id { get ; set ; }
public string Title { get ; set ; }
public List < string > Authors { get ; set ; }
}
public class KeyEqualityComparer < T > : IEqualityComparer < T >
{
private readonly Func < T, T, bool > comparer;
private readonly Func < T, object > keyExtractor;
// Allows us to simply specify the key to compare with: y => y.Id
public KeyEqualityComparer(Func < T, object > keyExtractor) : this (keyExtractor, null ) { }
// Allows us to tell if two objects are equal: (x, y) => y.Id == x.Id
public KeyEqualityComparer(Func < T, T, bool > comparer) : this ( null , comparer) { }
public KeyEqualityComparer(Func < T, object > keyExtractor, Func < T, T, bool > comparer)
{
this .keyExtractor = keyExtractor;
this .comparer = comparer;
}
public bool Equals(T x, T y)
{
if (comparer != null )
return comparer(x, y);
else
{
var valX = keyExtractor(x);
if (valX is IEnumerable < object > ) // The special case where we pass a list of keys
return ((IEnumerable < object > )valX).SequenceEqual((IEnumerable < object > )keyExtractor(y));
return valX.Equals(keyExtractor(y));
}
}
public int GetHashCode(T obj)
{
if (keyExtractor == null )
return obj.ToString().ToLower().GetHashCode();
else
{
var val = keyExtractor(obj);
if (val is IEnumerable < object > ) // The special case where we pass a list of keys
return ( int )((IEnumerable < object > )val).Aggregate((x, y) => x.GetHashCode() ^ y.GetHashCode());
return val.GetHashCode();
}
}
}
/// <summary>
/// 扩展方法
/// </summary>
public static class MyExtMethod
{
public static bool Contains < T > ( this IEnumerable < T > list, T item, Func < T, object > keyExtractor)
{
return list.Contains(item, new KeyEqualityComparer < T > (keyExtractor));
}
public static bool Contains < T > ( this IEnumerable < T > list, T item, Func < T, T, bool > comparer)
{
return list.Contains(item, new KeyEqualityComparer < T > (comparer));
}
}
}