C# Lambda表达式事例

C# Lambda表达式事例
Lambda表达式语法看上去真的很别扭,其实它就是便变种的匿名方法。其实你不用去管它的语法为什么是这样,你只要记住规则然后通过编写代码和比较慢慢的去体会就可以了,先来看匿名方法:
yuanl注释:首先使用集合初始化语法建立一个整型列表
List list = new List () { 1, 2, 3, 4, 5, 6, 7 };
//你也可以这样:List<int> list =new List<int>(){
1, 2, 3, 4, 5, 6, 7 };
//匿名方法的使用如下:
List ddNumbers = list.FindAll(
delegate(int i)
{
return (i % 2) != 0;
}

);//匿名方法到此结束,它的作用是筛选数据

foreach (var oddNumber in oddNumbers)//迭代输出数据
{
//输出奇数
Console.WriteLine(oddNumber);
}
上面的匿名方法,我们必须使用delegate来标注,而且还要保证输入参数的类型匹配,这种语法确实还是让人觉得不舒坦。
Lambda表达式是如何简化FindAll()方法的:
List ddNumbers = list.FindAll(i => (i % 2) != 0);
//等价于:
List ddNumbers = list.FindAll(i =>{if( (i % 2) != 0){return i;}});
解剖Lambda表达式
i => (i % 2) != 0
Lambda表达式的 => 标记(读作 goes to),它的前面是一个参数列表,后面是一个表达式或表达式块。
很明显,前面的参数列表并没有定义参数的类型(由编译器根据上下文推断出i是一个整型),所以它是隐式的。当然,我们也可以显示定义: (int i)=>(i%2)!=0);
我们这里参数列表只有一个参数,所以那个括号可以被省略。

Lambda表达式块语句形式:

List list = new List { 1,2,3,4,5,6,7};
//因为表达式需要多行代码来处理参数
切记表达式块的编写规范
var testList=list.FindAll(i=>{
Console.WriteLine("调用FindAll()");
Console.WriteLine("i的值是{0}",i);
bool isOdd=((i%2)!=0);
Console.WriteLine("是否是奇数:"+isOdd);
return isOdd; //Lambda 必须返回满足某个条件的Bool值
});//注意这里的分号
foreach (var a in testList)//var换作int 同样可以(只要符合类型转换规则就好)
{
Console.WriteLine(a);
}
再看下面的例子
使用 .net 2.0 的匿名方法来搜索字符串数组中包含 a 的字符串数组
static void Main(string[] args)
{
string[] list = new string[] { "abc", "12", "java" };//注意字符串数组的初始化规范
string[] yuanl = Array.FindAll(list,
delegate(string s)
{
    return s.IndexOf("a") >= 0;
}
//上面等价于:string[] yuanl = list.FindAll(delegate(string s){ return s.IndexOf("a") >= 0;});
foreach (string str in yuanl)
{
    Console.WriteLine(str);
}
Console.ReadLine();//这一句主要不同方式的调试环境
}
使用 .net 3.5 的Lambda表达式来搜索字符串数组中包含 a 的字符串数组
static void Main(string[] args)
{
string[] list = new string[] { "abc", "12", "java" };

string[] ll = Array.FindAll(list, s => (s.IndexOf("a") >= 0));
foreach (string str in ll)
{
Console.WriteLine(str);
}
Console.ReadLine();
}

从上述几个例子我们可以看出:
从代码书写角度,代码可读性角度来说:Lambda表达式 比匿名方法更简单了。
而 Lambda表达式 和 匿名方法都是干的同一件事情,让我们少写一个函数定义。函数的调用和函数的实现在一起完成了。
下面有两个参数的 Lambda表达式例子:
注:别看比较复杂,LINQ中实际把 下述代码中的 delegate ,DoSomeThing 替你做了,所以你写代码的时候只需要写
var t = DoSomeThing (7, 8, (x, y) => x * y); 这么一行。
public delegate T yuanl (T t1, T t2);
class Program
{
private static T DoSomeThing (T t1,T t2, yuanl match)//注意yuanl是一T型的委托类型
{
  return match(t1, t2);
}
static void Main(string[] args)
{
  var t = DoSomeThing (7, 8, (x, y) => x * y);
  Console.WriteLine(t);
  Console.ReadLine();
}
}
下面这些写法也是对的(你只需要修改Main函数中的代码,其他地方不需要动):
var t = DoSomeThing (7, 8, (int x, int y) => x * y);
var t = DoSomeThing ("7", "8", ( x, y) => x + y);//字符串拼接
或者我们写一个更复杂的: => 右边是一段语句块。
var t = DoSomeThing (7, 8, (x, y) => { if (x < 5) { return (x + 8) * y; } else { return y; } });
最前面的例子是一个参数的例子,我们就不举一个参数的例子了,下面举一个没有参数的例子:
public delegate void yuananl();
class Program
{
private static void DoSomeThing(yuananl match)
{
match();
}

static void Main(string[] args)
{
DoSomeThing(() => Console.WriteLine("jajaja"));
Console.ReadLine();
}
}

同样的,如果一些事件发生的时候我们只想做一点儿简单的事情的话,那么也可用Lambda表达式来实现:

   1: System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
   2: timer.Interval = 2000;
   3: timer.Tick += (sender, e) => { timer.Stop(); MessageBox.Show("Hello, Lambda Expression!"); };

我们来看一段比较完整的代码并以此结束对Lambda表达式的介绍吧:

   1: using System;
   2: using System.Collections.Generic;
   3:  
   4: namespace LambdaExpressionDemo
   5: {
   6:     class Program
   7:     {//定义一List对象_class
   8:         private static List<StudentData> _class = null;
   9:         //初始化该对象
  10:         static void Initialization()
  11:         {
  12:             _class = new List<StudentData>();
  13:  
  14:             _class.Add(new StudentData("ZeroCool", 24, GenderType.Male, 87));
  15:             _class.Add(new StudentData("Michael", 24, GenderType.Male, 93));
  16:             _class.Add(new StudentData("Frieda", 22, GenderType.Female, 98));
  17:             _class.Add(new StudentData("Somebody", 23, GenderType.Male, 81));
  18:         }
  19:  
  20:         static void Main(string[] args)
  21:         {
  22:             Initialization();
  23:  
  24:             if (_class == null || _class.Count == 0)
  25:             {
  26:                 throw new InvalidOperationException("The system initialization was failed.");//抛出异常
  27:             }
  28:            //筛选student数据
  29:             StudentData studentData = _class.Find(student => student.MathScore >= 90 && student.MathScore < 95);
  30:             RepresentData(studentData);//打印数据
  31:                //以下几条亦然
  32:             studentData = _class.Find(student => student.Name.Equals("ZeroCool", StringComparison.InvariantCultureIgnoreCase));
  33:             RepresentData(studentData);
  34:  
  35:             studentData = _class.Find(student => student.Age < 23);
  36:             RepresentData(studentData);
  37:  
  38:             Console.ReadLine();
  39:         }
  40:       //打印数据方法
  41:         static void RepresentData(StudentData student)
  42:         {
  43:             if (student == null)
  44:             {
  45:                 Console.WriteLine("No mached student.");
  46:  
  47:                 return;
  48:             }
  49:  
  50:             Console.WriteLine("Name:\t\t" + student.Name);
  51:             Console.WriteLine("Age:\t\t" + student.Age);
  52:             Console.WriteLine("Gender:\t\t" + student.Gender.ToString());
  53:             Console.WriteLine("Math Score:\t" + student.MathScore);
  54:             Console.WriteLine();
  55:         }
  56:     }
  57:  
  58:     public enum GenderType
  59:     {
  60:         Male = 0,
  61:         Female
  62:     }
  63:  
  64:     public class StudentData
  65:     {//注意下面字段、属性的定义规范和关联
  66:         private string _name = String.Empty;
  67:         public string Name
  68:         {
  69:             get { return this._name; }
  70:             set { this._name = value; }
  71:         }
  72:  
  73:         private int _age = 0;
  74:         public int Age
  75:         {
  76:             get { return this._age; }
  77:             set { this._age = value; }
  78:         }
  79:  
  80:         private GenderType _gender;
  81:         public GenderType Gender
  82:         {
  83:             get { return this._gender; }
  84:             set { this._gender = value; }
  85:         }
  86:  
  87:         private int _mathScore = 0;
  88:         public int MathScore
  89:         {
  90:             get { return this._mathScore; }
  91:             set { this._mathScore = value; }
  92:         }
  93:         //无参元的构造方法
  94:         public StudentData()
  95:         {
  96:         }
  97:          //带有参元的构造方法
  98:         public StudentData(string name, int age, GenderType gender, int mathScore)
  99:         {
 100:             this._name = name;
 101:             this._age = age;
 102:             this._gender = gender;
 103:             this._mathScore = mathScore;
 104:         }
 105:        //定义一委托类型
 106:         public delegate void EmptyDelegate();
 107:        委托变量(对象)挂接一方法
 108:         EmptyDelegate dl = () => Console.WriteLine();
 109:     }
 110: }

表达式树典型的声明形式是:

   1: Func<int, int> func = input => input * input;
   2: Expression<Func<int, int>> expression = input => input * input;

我们必须要使用的类型是System.Linq.Expressions命名空间中的Expression<T>,而这个T是定义表达式签名的委托的类型,这种把一个Lambda表达式看做一个数据结构而嵌套在另一个Lambda表达式中的做法使得现在的expression不是一个委托而是表达式树的数据结构了。接下来的问题是我们应该如何使用这个表达式树呢?请看完整的代码:

   1: static void Main(string[] args)
   2: {
   3:     Func<int, int> func = input => input * input;
   4:     Console.WriteLine(func(3).ToString());
   5:  
   6:     Expression<Func<int, int>> expression = input => input * input;
   7:     Func<int, int> fun = expression.Compile();
   8:     Console.WriteLine(fun(5).ToString());
   9:  
  10:     Console.ReadLine();
  11: }

正因为func是可执行代码而expression是数据,所以在使用方式上也会有所差别的,希望以上这些代码可以很好地让你理解Lambda表达式以及表达式树这两个概念。

最后总结一下:从delegate到lambda的演变

先看一个简单的delegate的例子

        public static bool IsOdd(int i)
        {
            return (i & 1) == 1;//判断输入的数据是否为零
        }

        public delegate bool NumberTester(int i);//声明一委托类型

        public static void PrintMatchingNumbers(int from, int to, NumberTester filter)
        {
            for (int i = from; i <= to; ++i)
            {
                if (filter(i))//判断
                {
                    Console.WriteLine(i);//输出不为零的数据
                }
            }
        }
调用时只需要执行:

PrintMatchingNumbers(1, 10, new NumberTester(IsOdd));//调用

等价于:PrintMatchingNumbers(1, 10,IsOdd);

很多情况下,我们只要执行只包含一个方法的代理。于是微软在.net 2.0里给我们提供了更方便的anonymous method,比如上面调用IsOdd的过程就能改写成下面这样:

             PrintMatchingNumbers(1, 10, delegate(int i)
                                        {
                                            return (i & 1) == 1;
                                         });

如此就避免了IsOdd的声明。微软提供匿名方法的道理是这样的:既然函数IsOdd只在一个地方被调用,那么直接在调用的地方实现岂不更省事儿。这样可以减少我们开发人员的输入。

接着到了3.0,微软更进了一步,他提出了lambda表达式。C#lambda表达式是这样定义的参数=> 表达式。于是,我们的代码变的更短:

              PrintMatchingNumbers(1, 10, i=>(i & 1) == 1);

也就是说lambda表达式作为一个函数并当作PrintMatchingNumbers的第三个参数来执行,而这个函数只要符合delegate NumberTester的签名就行。理所当然的lambda表达式可以附值给一个相同签名的delegate:
                NumberTester a =new NumberTester();
                a += i=>(i & 1) == 1;//注意这里不时数值运算而是方法挂接,a是委托对象

C# 3.0里甚至可以把声明和初始化以及赋值合到一块儿,简写成:

        NumberTester a = i=>(i & 1) == 1;

如果我们把NumberTester声明为泛型的delegate,如下:

        public delegate U NumberTester<T, U>(T i);

那么这个泛型的delegate应用就太广泛了,涵盖了一切有一个输入参数和一个返回值的函数,所以直接命名为Func更合适,即:
        public delegate U Func<T, U>(T i);

其实上面这个类型正是在.net Framework 3.0 中最重要的命名空间 System.Core里定义的。
这样,就可以写成

Func<int,bool> a = i=>(i & 1) == 1;

下面我们再回过头来看看使用这个delegatePrintMatchingNumbers,应该作相应的更改

        public static void PrintMatchingNumbers(int from, int to, Func<int, bool> filter)
        {
            for (int i = from; i <= to; ++i)
            {
                if (filter(i))
                {
                    Console.WriteLine(i);
                }
            }
        }

当然,由于泛型的的引入,此处还可以进一步的把类型抽离出来以表达这个意思:在一个范围内的任意的类型的变量,以指定的方式递增,通过判断打印符合条件的值。也就可以得到算法的复用。

完整的例子:

        public delegate U Func<T, U>(T i);

        public delegate void Func<T>(ref T i);

        static void Main(string[] args)

        {

            DateTime from = DateTime.Parse("2004/06/01");

            DateTime to = DateTime.Parse("2005/01/18");

            PrintMatchingT<DateTime>(from, to,

                delegate(ref DateTime i) { i = i.AddDays(1); },

                delegate(DateTime i) { return (i.Month + i.Day == 11);});

            Console.WriteLine();

            Console.Read();

        }

        public static void PrintMatchingT<T>(T from, T to, Func<T> incre, Func<T, bool> filter) where T : IComparable<T>
        {
            for (T i = from; (i.CompareTo(to) < 0); incre(ref i))
            {
                if (filter(i))
                {
                    Console.WriteLine(i);
                }
            }
        }

这个例子是在指定的时间范围内以天为递增量,判断代表月与日的数字加起来为
11的日子。当然具体到这个例子,我们也能把bool改成泛型的,只要保证实际传入的类型能够有效的转化成bool类型的,不然filter(i)是没法放到 if判断中的。

由此可见:混合使用泛型和匿名代理是可以写出很精简的代码,当然这也往往意味着需要写更多的注释来说明你的意图。

你可能感兴趣的:(lambda)