Lambda表达式, 可以让我们的代码更优雅.

时间:2023-01-10 19:00:58

在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));
}
}
}