16.C#:Lambda表达式和委托

我的理解:

1. 委托声明:delegate   string  MyDel(int  a); 这里, delegate是关键字(这里不要理解为委托类型,否则很难理解清楚委托),string是返回值, MyDel是委托类型(是 类型不是对象),
    创建委托对象并保存引用:string   fun2(int  a);  MyDel  del1,del2 = new MyDel(fun2);   del1和del2是委托变量,可以理解为C++中的函数指针,这一点很重要(这里应该理解为函数指针列表,因为可以向委托对象中添加函数,并且返回值与最后一个函数的返回值一致),还可以del1 = fun2;这是快捷语法,这样就更像C++中的函数指针了;new MyDel()可以理解为对委托对象变量创建引用的对象,类似于对一个类对象的初始化。
2.下面是我找的这篇博客的两个泛型委托声明:
    delegate  string  Func<T, F>(int a);   string为返回值
    delegate  bool  Func1<T, F, G>(int p, int j);   bool为返回值
--------------------------------------分割线------------------------------------
以下为引用的博客原文

本文来自:http://www.cnblogs.com/knowledgesea/p/3163725.html

前言

        1、天真热,程序员活着不易,星期天,也要顶着火辣辣的太阳,总结这些东西。

        2、夸夸lambda吧:简化了匿名委托的使用,让你让代码更加简洁,优雅。据说它是微软自c#1.0后新增的最重要的功能之一。

lambda简介

     lambda运算符:所有的lambda表达式都是用新的lambda运算符 " => ",可以叫他,“转到”或者 “成为”。运算符将表达式分为两部分,左边指定输入参数,右边是lambda的主体。

        lambda表达式:

               1.一个参数:param=>expr

               2.多个参数:(param-list)=>expr

        上面这些东西,记着,下面我们开始应用并阐述lambda,让你乐在其中。

lambda应用阐述    

         阐述这技术,我先上一个例子,然后再慢慢深入分析。例子如下:         

复制代码
namespace 阐述lambda
{
    public class Person
    {
        public string Name { get; set; }
        public int Age  {  get;set; }    
    }
    class Program
    {

        public static List<Person> PersonsList()
        {
            List<Person> persons = new List<Person>();
            for (int i = 0; i < 7; i++)
            {
                Person p = new Person() { Name = i + "儿子", Age = 8 - i, };
                persons.Add(p);                
            }
            return persons;
        }

        static void Main(string[] args)
        {
            List<Person> persons = PersonsList();
            persons = persons.Where(p => p.Age > 6).ToList();       //所有Age>6的Person的集合
            Person per = persons.SingleOrDefault(p => p.Age == 1);  //Age=1的单个people类
            persons = persons.Where(p => p.Name.Contains("儿子")).ToList();   //所有Name包含儿子的Person的集合
        }
    }
}
复制代码

     看啦上面的例子,相信你能看出它确实是个甜枣,呵呵,下面我们来看下(p=>p.Age>6)这样的表达式,到底是怎么回事。。

     首先我们看下委托  

复制代码
        //委托  逛超市
        delegate int GuangChaoshi(int a);
        static void Main(string[] args)
        {
            GuangChaoshi gwl = JieZhang;
            Console.WriteLine(gwl(10) + "");   //打印20,委托的应用
            Console.ReadKey();
        }
        
        //结账
        public static int JieZhang(int a)
        {
            return a + 10;
        }
复制代码

    再看表达式

复制代码
        //委托  逛超市
        delegate int GuangChaoshi(int a);
        static void Main(string[] args)
        {          
           // GuangChaoshi gwl = JieZhang;
            GuangChaoshi gwl = p => p + 10;
            Console.WriteLine(gwl(10) + "");   //打印20,表达式的应用
            Console.ReadKey();
        }        
       
复制代码

     委托跟表达式的两段代码,我们可以看出一些东东吧:其实表达式(p => p + 10;)中的 p 就代表委托方法中的参数,而表达式符号右边的 p+10,就是委托方法中的返回结果。 大侠绕道,小虾理解下。

    下面再上两个稍微复杂点的理解理解。

    1.多参数的 

复制代码
        //委托  逛超市
        delegate int GuangChaoshi(int a,int b);
        static void Main(string[] args)
        {            
            GuangChaoshi gwl = (p,z) => z-(p + 10);
            Console.WriteLine(gwl(10,100) + "");   //打印80,z对应参数b,p对应参数a
            Console.ReadKey();
        }
复制代码

        2. lambda主体运算复杂  

复制代码
        /// <summary>
        /// 委托  逛超市
        /// </summary>
        /// <param name="a">花费</param>
        /// <param name="b">付钱</param>
        /// <returns>找零</returns>
        delegate int GuangChaoshi(int a,int b);
        static void Main(string[] args)
        {
            GuangChaoshi gwl = (p, z) =>
            {
                int zuidixiaofei = 10;
                if (p < zuidixiaofei)
                {
                    return 100;
                }
                else
                {
                    return z - p - 10;
                }
           
            };
            Console.WriteLine(gwl(10,100) + "");   //打印80,z对应参数b,p对应参数a
            Console.ReadKey();
        }
复制代码

上面这些例子,好好理解下,下面我要介绍一个系统指定的 Fun<T>委托。

Func<T>委托

 T 是参数类型,这是一个泛型类型的委托,用起来很方便的。

 先上例子

复制代码
  static void Main(string[] args)
        {
            Func<int, string> gwl = p => p + 10 + "--返回类型为string";            
            Console.WriteLine(gwl(10) + "");   //打印‘20--返回类型为string’,z对应参数b,p对应参数a
            Console.ReadKey();
        }
复制代码

说明:我们可以看到,这里的p为int 类型参数, 然而lambda主体返回的是string类型的。

再上一个例子

复制代码
        static void Main(string[] args)
        {
            Func<int, int, bool> gwl = (p, j) =>
                {
                    if (p + j == 10)
                    {
                        return true;
                    }
                    return false;
                };
            Console.WriteLine(gwl(5,5) + "");   //打印‘True’,z对应参数b,p对应参数a
            Console.ReadKey();
        }
复制代码

说明:从这个例子,我们能看到,p为int类型,j为int类型,返回值为bool类型。

看完上面两个例子,相信大家应该明白啦Func<T>的用法:多个参数,前面的为委托方法的参数,最后一个参数,为委托方法的返回类型。

 lambda表达式树动态创建方法  

按 Ctrl+C 复制代码
static void Main(string[] args)
        {
            //i*j+w*x
            ParameterExpression a = Expression.Parameter(typeof(int),"i");   //创建一个表达式树中的参数,作为一个节点,这里是最下层的节点
            ParameterExpression b = Expression.Parameter(typeof(int),"j");
            BinaryExpression be = Expression.Multiply(a,b);    //这里i*j,生成表达式树中的一个节点,比上面节点高一级

            ParameterExpression c = Expression.Parameter(typeof(int), "w");
            ParameterExpression d = Expression.Parameter(typeof(int), "x");
            BinaryExpression be1 = Expression.Multiply(c, d);

            BinaryExpression su = Expression.Add(be,be1);   //运算两个中级节点,产生终结点

            Expression<Func<int, int, int, int, int>> lambda = Expression.Lambda<Func<int, int, int, int, int>>(su,a,b,c,d);

            Console.WriteLine(lambda + "");   //打印‘(i,j,w,x)=>((i*j)+(w*x))’,z对应参数b,p对应参数a

            Func<int, int, int, int, int> f= lambda.Compile();  //将表达式树描述的lambda表达式,编译为可执行代码,并生成该lambda表达式的委托;

            Console.WriteLine(f(1, 1, 1, 1) + "");  //打印2
            Console.ReadKey();
        }

按 Ctrl+C 复制代码

这段代码,放上来,仔细理解下,理解透彻啦,lambda表达式基本上也没什么啦。呵呵。。

算啦,我还是画个图算是结尾吧,以便于理解。

上段代码的lambda表达式树,图。

 

第二部分: C#中的委托,匿名方法和Lambda表达式

下面的文章通过实例讲述了三者关系,我没有详细看代码,但是感觉表述还行,尤其是:匿名方法只能在使用委托的时候创建(就是为这句话转载的/笑哭):文章来自:https://www.aliyun.com/zixun/content/3_12_524068.html

  在.NET中,委托,匿名方法和Lambda表达式很容易发生混淆。我想下面的代码能证实这点。下面哪一个First会被编译?哪一个会返回我们需要的结果?即Customer.ID=5.答案是6个First不仅被编译,并都获得正确答案,且他们的结果一样。如果你对此感到困惑,那么请继续看这篇文章。

class Customer 

public int ID { get; set; } 
public static bool Test(Customer x) 

return x.ID == 5; 


... 
List<Customer> custs = new List<Customer>(); 
custs.Add(new Customer() { ID = 1 }); 
custs.Add(new Customer() { ID = 5 }); 

custs.First(new Func<Customer, bool>(delegate(Customer x) { return x.ID == 5; })); 
custs.First(new Func<Customer, bool>((Customer x) => x.ID == 5)); 
custs.First(delegate(Customer x) { return x.ID == 5; }); 
custs.First((Customer x) => x.ID == 5); 
custs.First(x => x.ID == 5); 
custs.First(Customer.Test);

  什么是委托?

  现在你定义一个处理用户订单的购物车ShoppingCart类。管理层决定根据数量,价格等给客人折扣。做为其中的一部分,他们已经实现了处理订单时你要考虑一方面。不用考虑过多,你简单声明一个变量来保存有“吸引力的折扣”(magicDisCount),然后实现逻辑。

class Program { 
static void Main(string[] args) { 
new ShoppingCart().Process(); 


class ShoppingCart { 
public void Process() { 
int magicDiscount = 5; 
// ... 

}

  第二天,异想天开的管理层决定根据购买时间调整折扣。这个很简单,但需要你改动一点代码。

class ShoppingCart { 
public void Process() { 
int magicDiscount = 5; 
if (DateTime.Now.Hour < 12) { 
magicDiscount = 10; 


}

  接下来一段时间里,管理层又反复添加更多的折扣逻辑。这时你就会在心理抱怨“受够了”。那么我该怎么做才能把这些无聊的逻辑从我的代码中剥离出去,让该处理的人去处理呢?这时你要做的是移交或者委派给相应职能的别人。幸运的是,.NET为此提供了一种叫做“委托”的机制。

  委托

  如果你有C/C++编程背景,描述委托最好的方法是“函数指针”。对所有人来说,可以认为把委托传递给方法与把值或对象传递给方法一样。比如下面三行代码就表现出一样的基本原则:你在传递数据给Process处理而不是你自己使用。

// 给方法Process传递一个整形值 
Process( 5 ); 
// 给方法Process传递一个ArrayList的引用 
Process( new ArrayList() ); 
// 给方法Process传递一个方法的引用 
Process( discountDelegate );

  DiscountDelegate是什么?我如何创建?Process方法如何使用?首先如同声明一个类一样,声明一个委托类型。

delegate int DiscountDelegate();

  这句话的意思是我们有一个叫DiscountDelegate的委托类型,我们可以像使用类,结构体等一样使用它。它不需要数据参数,但返回一个整数值。像类一样,我们必须创建一个它的实例它才有意义。记住,创建一个委托实例实质上是创建一个方法的引用。创建实例时关键是要明白DiscountDelegate没有任何构造器,它有一个隐式的构造函数来构造一个与它相同签名的方法(没有传入参数,返回一个整数)。那你怎么给这个构造函数一个方法呢?.NET向你提供了一个向它名字一样简单的方法,你所做的只是忽略圆括号。

DiscountDelegate discount = new DiscountDelegate(class.method);

  在深入之前,先回到开始的例子,整理一个代码。我们会添加一个Calculator类来帮助我们处理折扣逻辑,并给我们的委托提供一些方法。

delegate int DiscountDelegate(); 

class Program { 
static void Main(string[] args) { 
Calculator calc = new Calculator(); 
DiscountDelegate discount = null; 
if (DateTime.Now.Hour < 12) { 
discount = new DiscountDelegate(calc.Morning); 

else if (DateTime.Now.Hour < 20) { 
discount = new DiscountDelegate(calc.Afternoon); 

else { 
discount = new DiscountDelegate(calc.Night); 

new ShoppingCart().Process(discount); 


class Calculator { 
public int Morning() { 
return 5; 

public int Afternoon() { 
return 10; 

public int Night() { 
return 15; 


class ShoppingCart { 
public void Process(DiscountDelegate discount) { 
int magicDiscount = discount(); 
// ... 

}

  正如你所见,在Calculator类中,我们为每个逻辑分支创建了一个方法。在Main方法中,我们创建一个Calculator实例和一个DiscountDelegate实例,并按照我们所期望的把它们整合在一起。

  太棒了,我们不用担心Process方法中的逻辑了,我们只需要简单得回调我们定义的委托。记住!我们不关心委托是如何创建的(或什么时间),我们就像调用其他方法一样调用它。如你所见,另一种理解委托的方法是,它延迟执行一个方法。Calculator方法在过去某个时间本选择,但不会执行,直到我们调用discount()的时候。现在看看我们的解决方案,这里仍然存在一些丑陋的代码。在Calculator类中,我们可以用一个不同的方法来返回替代每个有返回值得方法吗?答案是肯定的,让我们把这些乱糟糟的代码合并起来。

delegate int DiscountDelegate(); 

class Program { 
static void Main(string[] args) { 
new ShoppingCart().Process(new DiscountDelegate(Calculator.Calculate)); 


class Calculator { 
public static int Calculate() { 
int discount = 0; 
if (DateTime.Now.Hour < 12) { 
discount = 5; 

else if (DateTime.Now.Hour < 20) { 
discount = 10; 

else { 
discount = 15; 

return discount; 


class ShoppingCart { 
public void Process(DiscountDelegate discount) { 
int magicDiscount = discount(); 
// ... 

}

  这样子看起来更好点。你会注意到我们用一个静态的Calculate方法替换了所有原来的方法,在Main方法中也不用费心维护一个指向DiscountDelegate的引用。现在你明白了所有关于委托的东西了吗?在2004年.NET1.1中可以这么说,但是很不幸的是,这种框架自那以后更加成熟了。
  灯光,镜头,开始 或者我们需要Func!
  微软在.NET 2.0中引入了泛型,并提供了一个泛型委托:Action。老实说,我认为它远不够用。后来在.NET 3.5中,它为我们提供了一些我们不想定义的通用委托。他们扩展了Action,并添加了Func,二者唯一区别在于Func型方法有一个返回值而Action型方法没有。
  这意味着我们不需要声明自己的DiscountDelegate,可以用Func替代。为说明这些观点是如何工作的,我们来假设管理层又一次改变了我们的逻辑,我们需要提供一些特殊的折扣。很简单,我们将给Calculate方法传入一个bool型值。
  现在我们的委托签名变成Func。注意Calculate方法现在包含一个bool型参数,我们用一个bool值调用discount()。 

class Program { 
static void Main(string[] args) { 
new ShoppingCart().Process(new Func<bool, int>(Calculator.Calculate)); 



class Calculator { 
public static int Calculate(bool special) { 
int discount = 0; 
if (DateTime.Now.Hour < 12) { 
discount = 5; 

else if (DateTime.Now.Hour < 20) { 
discount = 10; 

else if (special) { 
discount = 20; 

else { 
discount = 15; 

return discount; 



class ShoppingCart { 
public void Process(Func<bool,int> discount) { 
int magicDiscount = discount(false); 
int magicDiscount2 = discount(true); 

}

  好像还算不错,我们又省了一行代码,这样算结束了吗?当然没有,我们甚至能省掉类型判断。只要我们传递的方法有严格签名的委托,.NET允许我们完全忽略掉显式创建Func<bool,int>。

//因为Process期望的方法有一个bool型输入参数和返回一个int值,所以下面这句话是正确的 
new ShoppingCart().Process(Calculator.Calculate);

  至此,首先通过忽略自定义委托,我们省略了代码;然后排出了明确的创建Func委托。我们能继续压缩代码行吗?到此我们才完成此文的一半,答案显然是“能”。

匿名方法

  匿名方法能够让你声明一个方法体而不需要给它指定一个名字。在接下来的场景里,它们以一个“普通的”方法存在;但是在你的代码中没有任何方法显式调用它。匿名方法只能在使用委托的时候创建,事实上,它们通过delegate关键字创建。

class Program { 
static void Main(string[] args) { 
new ShoppingCart().Process( 
new Func<bool, int>(delegate(bool x) { return x ? 10 : 5; } 
)); 

}

  正如你所见,我们完全删除了Calculator类的需求。你可以在打括号中添加任何其他方法中的逻辑。如果你在看它如何执行时有困难,那就把delegate(bool x)做为一个方法签名,而不是一个关键字。设想这段代码在一个类里,delegate(bool x){return 5;}是一个完整的合法方法声明(我们确实有一个返回值),恰好delegate是一个保留字,在这里,它让这个方法匿名。

  至此,我确信现在你知道这里我们甚至能压缩更多的代码。顺利成章的,我们能忽略显式声明Func委托的需要;.NET让我们使用delegate关键字更方便。

class Program { 
static void Main(string[] args) { 
new ShoppingCart().Process( 
delegate(bool x) { return x ? 10 : 5; } 
); 

}

  当把.NET方法做为委托参数时或处理时间时,就能看到匿名方法的真正用处。之前,你会为你所关注的所有可能行为创建了一个方法,现在你仅需以内联的方式创建它们,并可以避免污染你的命名空间。

// 创建一个匿名比  对方 法 
custs.Sort(delegate(Customer c1, Customer c2) { 
return Comparer<int>.Default.Compare(c1.ID, c2.ID); 
}); 

// 创建一个匿名事件 
button1.Click += delegate(object o, EventArgs e) { MessageBox.Show("Click!"); }; 

  Lambda 表达式

  MSDN中写道:“Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式树类型。你应当明白“用户创建委托”部分,但什么是“表达式”呢?老实说,表达式和表达式树不在此为讨论范围内。现在我们唯一需要明白的是,表达式是.NET程序运行时表示数据或对象的代码(C#代码)。引用Jon Skeet的话:“表达式树是一种表达逻辑,这样其他的代码可以查询的方法。当一个lambda表达式转换成一个表达式树,编译器不会发出了lambda表达式的白细胞介素,它会发出白细胞介素这将会建立一个表达式树表示相同的逻辑。”

  我们需要关注的是Lambda表达式替换匿名方法,和其他的特性。回顾我们最后例子,我们已经在一行代码里压缩了处理整个折扣算法的逻辑。

class Program { 
static void Main(string[] args) { 
new ShoppingCart().Process( 
delegate(bool x) { return x ? 10 : 5; } 
); 

}

  你相信我们能让这个更短吗?Lambda表达式用'=>'运算符表明什么参数传递给表达式。编译器进一步处理,允许我们忽略类型并自动替我们推断这些类型。如果你有2个或更多个参数,你需要用圆括号:(x,y)=>。如果只有一个,你设置不需要这样:x=>。

static void Main(string[] args) { 
Func<bool, int> del = x => x ? 10 : 5; 
new ShoppingCart().Process(del); 

// 更短啦... 
static void Main(string[] args) { 
new ShoppingCart().Process(x => x ? 10 : 5); 
}

  就是这样子。x被推断为bool型,并且有返回值,因为Process接收一个Func<bool,int>。如果我们想实现像之前那样的完整代码块,我们只需要加上大括号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值