C#教程:使用lambda表达式记录事件日志


使用List<T>和事件的简单例子

如果你记得List<T>当中的FindAll()方法, 其要求一个Predicate<T>参数并且返回一个新的列表, 该列表包含的所有元素都满足输入断言(predicate). Sort方法要求一个Comparison<T>参数然后按序排列整个列表. 以下的例子使用了lambda表达式提供了委托实例给每一个方法. 示例数据是不同电影的名称和发行年限. 我们将打印出原始的列表, 然后创建并打印出一个过滤的只包含老电影的列表, 并按名称排序打印出原始列表.

   1: class Film
   2: {
   3: public string Name { get; set; }
   4: public int Year { get; set; }
   5: public override string ToString()
   6: {
   7: return string.Format("Name={0}, Year={1}", Name, Year);
   8: }
   9: }
  10: ...
  11: var films = new List<Film>
  12: {
  13: new Film {Name="Jaws", Year=1975},
  14: new Film {Name="Singing in the Rain", Year=1952},
  15: new Film {Name="Some Like It Hot", Year=1959},
  16: new Film {Name="The Wizard of Oz", Year=1939},
  17: new Film {Name="It's a Wonderful Life", Year=1946},
  18: new Film {Name="American Beauty", Year=1999},
  19: new Film {Name="High Fidelity", Year=2000},
  20: new Film {Name="The Usual Suspects", Year=1995}
  21: };
  22: Action<Film> print = film => { Console.WriteLine(film); };
  23: films.ForEach(print);
  24: films.FindAll(film => film.Year < 1960)
  25: .ForEach(print);
  26: films.Sort((f1, f2) => f1.Name.CompareTo(f2.Name));
  27: films.ForEach(print);

代码的前半部分主要是数据的准备, 我们使用了匿名类, 其是一个泛型列表实例. 在开始使用这个列表之前, 我们创建了一个委托实例, 我们将会使用这个实例来打印列表.这就是为什么我使用了一个变量来保存该实例, 而不是每次都使用一个单独的lambda表达式. 它仅仅打印了一个元素, 但是将它传递给List<T>.ForEach方法我们可以整个列表元素都输出到控制台.

我们打印的第一个列表是没有任何编辑的原始列表, 接下来我们查找并打印出了所有发行年限在1960年之前的电影. 我们使用了另外一个lambda表达式来完成这项工作, 列表中的每一个项都会执行该表达式——它用于决定该元素是否应该出现在新的过滤列表当中. 源代码当中使用了lambda表达式作为方法参数, 然而编译器实际上是类似下面代码一样创建了一个方法:

   1: private static bool SomeAutoGeneratedName(Film film)
   2: {
   3: return film.Year < 1960;
   4: }

FindAll方法调用将会类似下面这样:

   1: films.FindAll(new Predicate<Film>(SomeAutoGeneratedName))

这里对于lambda表达式的支持与C# 2中的匿名方法一致, 这就是编译器的聪明之处. (实际上, 微软的编译器在这个例子中甚至还更加聪明——它甚至能够意识到如果代码再次被调用, 委托类型可能会再次被使用, 因此会将它缓存.)

排序部分同样也是使用了一个lambda表达式, 其通过名字进行了比较. 我不得不承认显式调用CompareTo有点丑陋, 在下面一节当中我们将看到如何使用OrderBy扩展方法来更简洁的排序.

使用lambda表达式记录事件日志

   1: static void Log(string title, object sender, EventArgs e)
   2: {
   3:     Console.WriteLine("Event: {0}", title);
   4:     Console.WriteLine(" Sender: {0}", sender);
   5:     Console.WriteLine(" Arguments: {0}", e.GetType());
   6:     foreach (PropertyDescriptor prop in
   7:     TypeDescriptor.GetProperties(e)){
   8:     string name = prop.DisplayName;
   9:     object value = prop.GetValue(e);
  10:     Console.WriteLine(" {0}={1}", name, value);
  11: }
  12:  
  13: ...
  14: Button button = new Button();
  15: button.Text = "Click me";
  16: button.Click += (src, e) => { Log("Click", src, e); };
  17: button.KeyPress += (src, e) => { Log("KeyPress", src, e); };
  18: button.MouseClick += (src, e) => { Log("MouseClick", src, e); };
  19: Form form = new Form();
  20: form.AutoSize=true;
  21: form.Controls.Add(button);
  22: Application.Run(form);

上述的代码使用了lambda表达式来传递事件名和参数到Log方法, 该方法记录了事件的一些详细信息. 我们并没有记录源事件的所有信息, 而仅仅是调用了ToString()重载方法, 因为控件本身承载了大量的信息. 实际上, 我们使用了反射来遍历了属性的descriptor信息, 这里是一个可能的输出结果:

   1: Event: Click
   2: Sender: System.Windows.Forms.Button, Text: Click me
   3: Arguments: System.Windows.Forms.MouseEventArgs
   4: Button=Left
   5: Clicks=1
   6: X=53
   7: Y=17
   8: Delta=0
   9: Location={X=53,Y=17}
  10: Event: MouseClick
  11: Sender: System.Windows.Forms.Button, Text: Click me
  12: Arguments: System.Windows.Forms.MouseEventArgs
  13: Button=Left
  14: Clicks=1
  15: X=53
  16: Y=17
  17: Delta=0
  18: Location={X=53,Y=17}

所有的这些不通过lambda表达式当然也可以做到, 不过使用lambda表达式相比其他的做法更加简洁.

使用List<T>和事件的简单例子

如果你记得List<T>当中的FindAll()方法, 其要求一个Predicate<T>参数并且返回一个新的列表, 该列表包含的所有元素都满足输入断言(predicate). Sort方法要求一个Comparison<T>参数然后按序排列整个列表. 以下的例子使用了lambda表达式提供了委托实例给每一个方法. 示例数据是不同电影的名称和发行年限. 我们将打印出原始的列表, 然后创建并打印出一个过滤的只包含老电影的列表, 并按名称排序打印出原始列表.

   1: class Film
   2: {
   3: public string Name { get; set; }
   4: public int Year { get; set; }
   5: public override string ToString()
   6: {
   7: return string.Format("Name={0}, Year={1}", Name, Year);
   8: }
   9: }
  10: ...
  11: var films = new List<Film>
  12: {
  13: new Film {Name="Jaws", Year=1975},
  14: new Film {Name="Singing in the Rain", Year=1952},
  15: new Film {Name="Some Like It Hot", Year=1959},
  16: new Film {Name="The Wizard of Oz", Year=1939},
  17: new Film {Name="It's a Wonderful Life", Year=1946},
  18: new Film {Name="American Beauty", Year=1999},
  19: new Film {Name="High Fidelity", Year=2000},
  20: new Film {Name="The Usual Suspects", Year=1995}
  21: };
  22: Action<Film> print = film => { Console.WriteLine(film); };
  23: films.ForEach(print);
  24: films.FindAll(film => film.Year < 1960)
  25: .ForEach(print);
  26: films.Sort((f1, f2) => f1.Name.CompareTo(f2.Name));
  27: films.ForEach(print);

代码的前半部分主要是数据的准备, 我们使用了匿名类, 其是一个泛型列表实例. 在开始使用这个列表之前, 我们创建了一个委托实例, 我们将会使用这个实例来打印列表.这就是为什么我使用了一个变量来保存该实例, 而不是每次都使用一个单独的lambda表达式. 它仅仅打印了一个元素, 但是将它传递给List<T>.ForEach方法我们可以整个列表元素都输出到控制台.

我们打印的第一个列表是没有任何编辑的原始列表, 接下来我们查找并打印出了所有发行年限在1960年之前的电影. 我们使用了另外一个lambda表达式来完成这项工作, 列表中的每一个项都会执行该表达式——它用于决定该元素是否应该出现在新的过滤列表当中. 源代码当中使用了lambda表达式作为方法参数, 然而编译器实际上是类似下面代码一样创建了一个方法:

   1: private static bool SomeAutoGeneratedName(Film film)
   2: {
   3: return film.Year < 1960;
   4: }

FindAll方法调用将会类似下面这样:

   1: films.FindAll(new Predicate<Film>(SomeAutoGeneratedName))

这里对于lambda表达式的支持与C# 2中的匿名方法一致, 这就是编译器的聪明之处. (实际上, 微软的编译器在这个例子中甚至还更加聪明——它甚至能够意识到如果代码再次被调用, 委托类型可能会再次被使用, 因此会将它缓存.)

排序部分同样也是使用了一个lambda表达式, 其通过名字进行了比较. 我不得不承认显式调用CompareTo有点丑陋, 在下面一节当中我们将看到如何使用OrderBy扩展方法来更简洁的排序.

使用lambda表达式记录事件日志

   1: static void Log(string title, object sender, EventArgs e)
   2: {
   3:     Console.WriteLine("Event: {0}", title);
   4:     Console.WriteLine(" Sender: {0}", sender);
   5:     Console.WriteLine(" Arguments: {0}", e.GetType());
   6:     foreach (PropertyDescriptor prop in
   7:     TypeDescriptor.GetProperties(e)){
   8:     string name = prop.DisplayName;
   9:     object value = prop.GetValue(e);
  10:     Console.WriteLine(" {0}={1}", name, value);
  11: }
  12:  
  13: ...
  14: Button button = new Button();
  15: button.Text = "Click me";
  16: button.Click += (src, e) => { Log("Click", src, e); };
  17: button.KeyPress += (src, e) => { Log("KeyPress", src, e); };
  18: button.MouseClick += (src, e) => { Log("MouseClick", src, e); };
  19: Form form = new Form();
  20: form.AutoSize=true;
  21: form.Controls.Add(button);
  22: Application.Run(form);

上述的代码使用了lambda表达式来传递事件名和参数到Log方法, 该方法记录了事件的一些详细信息. 我们并没有记录源事件的所有信息, 而仅仅是调用了ToString()重载方法, 因为控件本身承载了大量的信息. 实际上, 我们使用了反射来遍历了属性的descriptor信息, 这里是一个可能的输出结果:

   1: Event: Click
   2: Sender: System.Windows.Forms.Button, Text: Click me
   3: Arguments: System.Windows.Forms.MouseEventArgs
   4: Button=Left
   5: Clicks=1
   6: X=53
   7: Y=17
   8: Delta=0
   9: Location={X=53,Y=17}
  10: Event: MouseClick
  11: Sender: System.Windows.Forms.Button, Text: Click me
  12: Arguments: System.Windows.Forms.MouseEventArgs
  13: Button=Left
  14: Clicks=1
  15: X=53
  16: Y=17
  17: Delta=0
  18: Location={X=53,Y=17}

所有的这些不通过lambda表达式当然也可以做到, 不过使用lambda表达式相比其他的做法更加简洁.


« 
» 
快速导航

Copyright © 2016 phpStudy | 豫ICP备2021030365号-3