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