本文来自:http://wenku.baidu.com/link?url=o9Xacr4tYocCPhivayRQXfIc9kOZeWBwPn2FZfeF19P4-8YX5CMXs74WB-Y8t0S96cb8x7cNpL-2A4OKd3wiet89u0aYszge7pn4U_WfJUq

1.委托

(1)委托的本质 

委托实际上就是指向函数的指针。在C#中委托是一种类型,定义一个委托就是定义一个新的类,它与类的地位是一样的,所以可以定义类的地方都可以定义委托!实际上,使用delegate关键字定义的委托继承了System.MulticastDelegate类,而System.MulticastDelegate类又继承了System.Delegate。

Delegate:表示委托,委托是一种数据结构,它引用静态方法或引用类实例及该类的实例方法。

MulticastDelegate:表示多路广播委托;即,其调用列表中可以拥有多个元素的委托。

 (2)使用委托的示例 

delegate string DelegateMethod(int a, int b);//定义委托

class Program     {

static void Main(string[] args)         {

DelegateMethod dm = new DelegateMethod(Method);      //或:DelegateMethod dm = Method

Console.WriteLine(dm(10, 20));//执行委托

Console.Read();

} 
        private static string Method(int a, int b)

{ 
            return "相加结果:" + (a + b);

}

} 
(3)委托的Invoke与BeginInvoke方法 

1、Invoke方法

实际上,给委托实例提供圆括号(dm(10, 20))与调用委托的Invoke(dm.Invoke(10, 20))方法效果完全相同:

delegate string DelegateMethod(int a, int b);//定义委托

class Program

{  
        static void Main(string[] args)

{ 
            DelegateMethod dm = Method;

Console.WriteLine(dm.Invoke(10, 20));//执行委托

Console.Read();

} 
        private static string Method(int a, int b)

{ 
            return "相加结果:" + (a + b);

}

} 
2、BeginInvoke方法 

调用委托实例的BeginInvoke方法就是开启一个新的线程执行委托实例指向的方法。

delegate void DelegateMethod(int a, int b);//定义委托

class Program

{

static void Main(string[] args)

{ 
            DelegateMethod dm = Method;

for (int i = 0; i < 10; i++)

{ 
                dm.BeginInvoke(i, 20, null, null);//异步调用委托

} 
            Console.Read();

} 
     private static void Method(int a, int b)

{

Console.WriteLine("相加结果:" + (a + b));  //执行委托

}

}

BeginInvoke方法有三种参数:

第一种参数是委托实例指向的方法的参数(可能有多个);

第二种参数的类型是AsyncCallback,AsyncCallback是一个委托类型,其定义是delegate void AsyncCallback(IAsyncResult ar),当调用BeginInvoke方法的委托实例异步执行完成时,

就会执行该参数指向的方法;

第三种参数是object类型,主要是向第二种参数传递一些值,一般可以传递被调用方法的委托,这个值可以使用IAsyncResult.AsyncState属性获得。(具体请参考“C#多线程”)     

delegate void DelegateMethod(int a, int b);//定义委托

class Program

{

static void Main(string[] args)

{ 
            DelegateMethod dm = Method; 
            dm.BeginInvoke(10, 20, MethodCompleted, null);//异步调用委托

Console.Read();

} 
        //异步委托 
        private static void Method(int a, int b)

{ 
            Console.WriteLine("相加结果:" + (a + b));

Thread.Sleep(3000);

} 
        //回调函数 
        private static void MethodCompleted(IAsyncResult ar)

{ 
            Console.WriteLine("休眠结束!");

}

} 
3、委托的EndInvoke方法 

如果调用了委托实例的BeginInvoke方法,就可以通过EndInvoke方法获得委托实例指向的方法的返回值,或是确定指向的方法已经被成功调用。(具体请参考“C#多线程”)    

 delegate string DelegateMethod(int a, int b);//定义委托

class Program

{ 
        static void Main(string[] args)

{ 
            DelegateMethod dm = Method;

IAsyncResult ar = dm.BeginInvoke(10, 20, null, null);//异步调用委托

string result = dm.EndInvoke(ar);//等待委托异步调用结束

Console.WriteLine(result);//输出返回值

Console.Read();

} 
        //异步委托 
        private static string Method(int a, int b)

{ 
            Thread.Sleep(3000); 
            return "相加结果:" + (a + b);

}

} 
(4)Action<T>、Func<T>与Predicate<T>(泛型委托) 

除了为每个参数和返回类型定义一个新委托之外,还可以使用Action<T>与Func<T>泛型委托。使用泛型委托主要的优势是可以省略委托的定义。

1、Action<T>无返回值的泛型委托 

泛型Action<T>委托表示引用一个void返回类型的方法,这个委托类可以有多个(≥0)参数且参数的类型可以不同(最多可以有16种不同类型的参数)。

class Program

{

static void Main(string[] args)

{ 
            Action<int,int> dm = Method;//委托实例

dm(10,20);//调用委托

Console.Read();

} 
        private static void Method(int a, int b)

{ 
            Console.WriteLine("相加结果:" + (a + b));

}

} 
2、Func<T>带有返回值的泛型委托 
泛型Func<T>委托与Action<T>委托类似,不同点是Func<T>允许调用带返回值类型的方法,而且其返回值类型的指定是Func<T1,T2,T3...Tn>中的最后一个类型(Tn),即Tn就是其返回值,其它类型都表示参数的类型。(最多可以有16种参数类型和一个返回值类型)

class Program

{ 
        static void Main(string[] args)

{ 
            Func<int, int, string> dm = Method;//委托实例

string result = dm(10, 20);//调用委托

Console.WriteLine(result);//输出返回值

Console.Read();

} 
        private static string Method(int a, int b)

{ 
            return "相加结果:" + (a + b);

}

} 
3、Predicate<T>委托(常用于集合参数,Array 和 List ) 
Predicate<T>泛型委托,只能接受一个传入参数,返回值为bool类型。

class Program

{ 
        static void Main(string[] args)

{ 
             Predicate<int> dm = Method;//委托实例

if (dm(12))//调用委托

{ 
                Console.WriteLine("偶数");

}

else

{ 
                Console.WriteLine("奇数");

} 
            Console.Read();

} 
        private static bool Method(int a)

{ 
             if(a%2==0)

{ 
                return true;

} 
               return false;

}

}

其他使用到Predicate 有

Array.Find , Array.FindAll , Array.Exists , Array.FindLast , Array.FindIndex ..... 
  List<T>.Find , List<T>.FindAll , List<T>.Exists , List<T>.FindLast , List<T>.FindIndex ..... 
延伸:
  除了上面提到的外,你完全可以使用Predicate 定义新的方法,来加强自己代码。


public class GenericDelegateDemo
{
    List<String> listString = new List<String>()
    {
        "One","Two","Three","Four","Fice","Six","Seven","Eight","Nine","Ten"
    };     public String GetStringList(Predicate<String> p)
    {
        foreach(string item in listString)
        {
            if (p(item))
                return item;
        }
        return null;
    }     public bool ExistString()
    {
        string str = GetStringList((c) => { return c.Length <= 3 && c.Contains('S'); });
        if (str == null)
            return false;
        else
            return true;
    }
}

(5)多播委托 
前面的委托只包含一个方法,但是委托也可以包含多个方法,这种委托称为多播委托。如果调用多播委托就可以按顺序连续调用多个方法,为此委托的定义就必须返回void,否

则就只能得到委托调用的最后一个方法的结果。多播委托可以识别“+、-、+=、-=”运算符。 
    class Program

{

static void Main(string[] args)

{ 
            Action<int> dm = null; //委托实例

for (int i = 0; i < 20; i++)

{ 
                if (i % 2 == 0)

{ 
                    dm += Method1;

}

else

{ 
                    dm += Method2;

}

} 
            dm(123456);//调用多播委托

Console.Read();

} 
        private static void Method1(int a)

{ 
            Console.WriteLine("委托1:" + a);

} 
        private static void Method2(int a)

{ 
            Console.WriteLine("委托2:" + a);

}

} 
(6)使用委托定义匿名方法 
   
 class Program

{ 
        static void Main(string[] args)

{ 
            Action<int> dm = delegate(int a) {

                Console.WriteLine(a.ToString());             

};//匿名方法

dm(123456);//调用委托指向的匿名方法

Console.Read();

}

} 
2.Lambda表达式 

(1)Lambda表达式本质  

Lambda表达式的本质是一个匿名函数,Lambda表达式只能与委托配合使用,其优势在可以很方便的定义匿名方法。所有Lambda表达式都使用Lambda运算符=>,该运算符读

作"goes to"。Lambda运算符的左边是输入参数(如果有),右边是表达式或语句块。

 (2)Lambda表达式使用示例 

    class Program    

 { 
        static void Main(string[] args)         

        { 
            //标准的Lambda格式 
            Func<int, int, string> sum0 = (int a, int b) => { return "和是:" + (a + b); };             

            Console.WriteLine(sum0(10, 20));             //简写格式 

            Func<int, int, string> sum1 = (a, b) => "和是:" + (a + b);             

           Console.WriteLine(sum1(30, 40)); 

Action<int, int> sum2 = (a, b) => Console.WriteLine("和是:" + (a + b));

sum2(50, 60);

Console.Read();

}

} 
(3)Lambda表达式语法 

1、表达式Lambda语法(参数语法) 

如果只有一个输入参数时,括号可以省略,如:(x) => x * x 等于 x => x * x

如果具有一个以上的输入参数,必需加上括号,如:(x, y) => x == y 可以显式指定输入参数的类型,如:(int x, string s) => s.Length > x

也可以没有任何输入参数,如:() => Console.WriteLine("无参数的Lambda表达式")

2、语句Lambda语法(函数体语法) 

语句写在大括号中,如:

Action<int, int> sum = (a, b) => { int i = a = +b; Console.Write(i); }; 只有一条语句时,可以省略大括号“{}”,如:

Action<int, int> sum = (a, b) => Console.Write(a + b); 当匿名方法有返回值时,可以使用return,如:

Func<int, int, string> sum0 = (a, b) => { return "和是:" + (a + b); }; 当匿名方法有返回值,且只有一条语句时,可以省略大括号“{}”和return,如:

Func<int, int, string> sum1 = (a, b) => "和是:" + (a + b);

注意:当有返回值时,语句Lambda中使用了大括号就一定要使用return关键字返回;反之使用了return关键字返回结果就一定要使用大括号。即:return关键字与大括号必须同时省略!

 
3、类型猜测 

当编写一个Lambda式的时候,我们通常不需要明确指定输入参数的类型。因为编译器会根据Lambda体的实现,以及委托的定义来猜测类型。如:如果要从一个List<int>中删除小于100的元

素

List<int> list = new List<int>();

list.RemoveAll(i => i < 100);//i会被猜测为int 通常的猜测规则如下:

Lambda式必须包含与委托定义中相等数量的输入参数;

每个Lambda式的输入参数必须能够隐式转换成委托定义中所要求的输入参数; Lambda式的返回值必须能够隐式转换成委托定义中的返回值。


4、Lambda式中的变量作用域 


在Lambda式定义中可以引用外部变量。只要是在定义处能够访问到的变量,都可以在Lambda式中引用。所有会被引用的外部变量必须在Lambda式定义之前被显式赋值。

变量作用域的规则:

1)、被“捕获”的变量在委托的生命周期结束前都不会被垃圾回收;

2)、在Lambda式内部定义的变量对外不可见;

3)、Lambda式无法直接捕获一个具有ref或out描述的参数变量;

4)、Lambda式中的return语句不会导致当前所在的方法返回;

5)、Lambda式中不允许包含会导致跳当前执行范围的goto,break 或 continue语句。

委托、Lambda表达式的更多相关文章

  1. 委托 lambda表达式浅显理解

    方法不能跟变量一样当参数传递,怎么办,C#定义了委托,就可以把方法当变量一样传递了,为了简单,匿名方法传递,省得再声明方法了:再简单,lambda表达式传递,比匿名方法更直观. public dele ...

  2. Unity C# 多态 委托 事件 匿名委托 Lambda表达式 观察者模式 .NET 框架中的委托和事件

    一.多态 里氏替换原则: 任何能用基类的地方,可以用子类代替,反过来不行.子类能够在基类的基础上增加新的行为.面向对象设计的基本原则之一. 开放封闭原则: 对扩展开放,意味着有新的需求或变化时,可以对 ...

  3. 委托/lambda表达式/事件

    委托 委托是执行安全的类,它的使用方式与类类似(即都需要定义再实例化),不同在于,类在实例化之后叫对象或类的实例,但委托在实例化后仍叫委托,委托可以把函数作为参数传递. 语法声明: delegate ...

  4. 委托+内置委托方法+多播委托+lambda表达式+事件

    委托概念:如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托: 在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字 ...

  5. C#编程 委托 Lambda表达式和事件

    委托 如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托 在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方 ...

  6. C#学习笔记三(委托·lambda表达式和事件,字符串和正则表达式,集合,特殊的集合)

    委托和事件的区别 序号 区别 委托 事件 1 是否可以使用=来赋值 是 否 2 是否可以在类外部进行调用 是 否 3 是否是一个类型 是 否,事件修饰的是一个对象 public delegate vo ...

  7. C# 委托(delegate)、泛型委托和Lambda表达式

    目录 # 什么是委托 # 委托声明.实例化和调用 1.声明 2.委托的实例化 3.委托实例的调用 4.委托完整的简单示例 #泛型委托 1.Func委托 2.Action委托 3.Predicate委托 ...

  8. Lambda表达式演变

    Lambda表达式是一种匿名函数.   演变步骤:   一般的方法委托 => 匿名函数委托 => Lambda表达式   Lambda表达式其实并不陌生,他的前生就是匿名函数,所以要谈La ...

  9. 理解Lambda表达式和闭包

    了解由函数指针到Lambda表达式的演化过程 Lambda表达式的这种简洁的语法并不是什么古老的秘法,因为它并不难以理解(难以理解的代码只有一个目的,那就是吓唬程序员) #include " ...

  10. 用五分钟重温委托,匿名方法,Lambda,泛型委托,表达式树

    这些对老一代的程序员都是老生常谈的东西,没什么新意,对新生代的程序员却充满着魅力.曾经新生代,好多都经过漫长的学习,理解,实践才能掌握委托,表达式树这些应用.今天我尝试用简单的方法叙述一下,让大家在五 ...

随机推荐

  1. Unity四种路径总结

    四种路径的权限:                                            Application.dataPath 包含游戏数据文件夹的路径(只读) Applicatio ...

  2. New Year Table(几何)

    New Year Table Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Sub ...

  3. Duanxx的STM32学习:STM32命名规则

  4. Lua内存泄漏应对方法[转]

    转自http://blog.csdn.net/xocoder/article/details/42685685 由于目前正在负责的项目是一个二次开发项目,而且留给我们的代码质量实在让人无力吐槽,所以遇 ...

  5. BOOST 线程完全攻略 - 扩展 - 事务线程

    扩展threadtimermoduleexceptionsocket 什么叫事务线程 举个例子: 我们写一个IM客户端的登录子线程,则该子线程会有这么几个事务要处理 No.1 TCP Socket物理 ...

  6. 阻止IOS自动识别页面上的电话号码、email地址

    之前写页面的时候碰到一个很恶心的情况,在6P上数字自动变色,后来找了一些资料: 在iOS的浏览器上,他们有时候会有一些“自作聪明”,自动把页面上的一串数字识别成电话号码,这样用户不小心点击这串数字,就 ...

  7. javascript 多图无缝切换

    思路只要是ul移动前,首先将当前显示的li克隆岛ul最后,当每次运动执行完毕后,再将前面的li删除,如此循环. <!DOCTYPE html> <html> <head& ...

  8. 使用Fiddler捕获Java程序中的HTTP请求

      默认Java程序是不支持Fiddler获取请求的,因此需要通过设置代理来实现.   Fiddler的端口是8888,若Fiddler没有运行,则会抛出异常.   HttpClient4.5示例: ...

  9. 在SQL SERVER中批量替换字符串

    update [表名] set [字段名]=replace([字段名],'被替换原内容','替换后内容')

  10. MIT scheme入门使用

    在win7下可安装MIT-GUN scheme, 点开后有两个界面:一个交互式命令行界面:一个Edwin界面.    在命令行界面按Ctrl-G可以开始输入.在Edwin界面,输入完整命令后按Ctrl ...