您的位置:首页 > 编程语言 > C#

C# 代理 λ表达式和事件(学习笔记)

2011-01-20 12:41 288 查看
1 概述:

代理(或者叫委托)是.NET版本的方法地址,类比于C++的函数指针,它是一个定义了返回值类型和参数值类型的类型安全的类(有点绕),代理类可以包含一个或者多个对方法的引用。

λ表达式直接和代理相关,当参数是代理类型时,你可以使用λ表达式来实现一个被代理所引用的方法。

2 声明代理:

1)delegate void MethodInvoker(int x);这里定义了一个名为MethodInvoker的代理,它保存了一个接受int类型的参数,返回值为void的方法引用。

2)代理以继承自System..MulticastDelegate的类的形式实现,基类为System.Delegate。C#编译器隐藏了代理类的操作细节。

3 使用代理:

1)创建代理的实例。

2)不论是静态方法还是不同类的实例方法,只要方法签名和代理匹配,就可以通过代理来调用。

3)当代理对象指向一个实例方法时,它不仅保存对该实例方法的引用,而且还保存该方法所属实例的引用,可以通过System.Delegate类的Target属性来获得该实例,如果是静态方法,则Target属性为null。

4泛型代理Action<T>和Func<T>

1) Action<T>代理匹配没有返回值的方法或者说返回值为void的方法,它可以包含16个不同类型的参数。

比如Action匹配没有参数的方法,Action< in T1, in T2>匹配有两个参数的方法。

2) Func<T>代理匹配有返回值的方法,它也可以包含16个不同类型的参数。

比如:Func<out TResult>匹配没有参数,但返回值为TResult类型的方法。

Func<in T ,out TResult>匹配只有一个参数T,返回值为TResult类型的方法。

3)contravariance和covariance:

Action<T>支持contravariance:

比如:Action<object> x = ...;

Action<string> y = x;

Func<T>支持covariance:

比如:Func<string> x = ...;

Func<object> y = x;

5 多路代理的问题:

1)当使用多路代理时,依次调用相应的代理方法,如果其中有一个方法报异常,迭代将会中止。解决方法是:调用Delegate类的GetInvocationList()方法,返回Delegate的数组,然后依次遍历数组,进行异常的控制。

例如:

static void Main()

{

Action d1 = One;

d1 += Two;

Delegate[] delegates = d1.GetInvocationList();

foreach (Action d in delegates)

{

try

{

d();

}

catch (Exception)

{

Console.WriteLine("Exception caught");

}

}

}

6 代理和接口:

1)可以使用代理解决的问题,同样也可以使用接口。

2)下面的情况优先使用代理:

(1)接口中只有一个方法。

(2)需要使用多路代理。

(3)同一个接口需要多次不同的实现。

7代理的兼容性:

1)即使两个代理的方法签名一样,它们也是不兼容的。

比如: delegate void D1();

delegate void D2();

D1 d1=method1;

D2 d2=d1;//编译错误。

但是:D2 d2=new D2(d1);//可行。

2)如果两个代理的实例指向相同的方法,则两代理实例相等。

3)参数的兼容性,代理可以有比目标方法更具体的参数类型。

比如:

delegate void StringAction (string s);

class Test

{

static void Main()

{

StringAction sa = new StringAction (ActOnObject);

sa ("hello");

}

static void ActOnObject (object o)

{

Console.WriteLine (o); // hello

}

}//当方法调用时,string类型隐式的向上转换成object类型。

4)返回值类型的兼容性:目标方法的返回值可以比代理的返回值更具体。

比如:

delegate object ObjectRetriever();

class Test

{

static void Main()

{

ObjectRetriever o = new ObjectRetriever (RetriveString);

object result = o();

Console.WriteLine (result); // hello

}

static string RetriveString() { return "hello"; }

}

7 匿名方法:

1)它是使用代理的另一种方式,通常用作为代理的参数。

static void Main()

{

string temp = "Hello";

Func < string, string > anonDel = delegate(string param)

{

param += temp;

param += " Terry !";

return param;

};

Console.WriteLine(anonDel("Begin:"));

}

2)使用匿名方法的优缺点:

优点:减少了代码量。

缺点:(1)在匿名方法内部不能使用跳转语句(break,goto和continue)来指向匿名方法的外部。反之依然,不能使用这些跳转语句指向匿名方法的内部。

(2)在匿名方法内部不能访问不安全的代码。

(3)匿名方法外部使用的ref 和out参数,不能在匿名方法内部被访问,而其外部定义的变量可以访问。

8 λ表达式:

1)不论何时当你需要代理类型的参数时,均可以使用λ表达式。

static void Main()

{

string temp = "Hello";

Func < string, string > lambda = param=>

{

param += temp;

param += " Terry !";

return param;

};

Console.WriteLine(lambda("Begin:"));

}

2) λ表达式的参数问题:当有多个参数时,将参数以逗号分隔,放入括号中。

3)需要注意的是,当λ表达式包含一行语句时,可以省略大括号和return语句,如果包含多行语句,这大括号和return语句不可以省略。

Func<double, double> square = x => x * x;

Func<double, double> square = x =>

{

return x * x;

}以上完全等价。

3) λ表达式的变量捕捉问题,在for或者foreach中变量,C#将会把它们当做外部变量来处理。

比如:

Action[] actions = new Action[3];

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

actions [i] = () => Console.Write (i);

foreach (Action a in actions) a(); // 333

9事件:

1) 事件基于代理,并提供了一个发布和订阅代理的途径。比如在windows应用中,按钮的点击事件,这种事件就是一个代理,当单击事件发生时,一个带有代理类型参数的方法将被调用。

2) 标准的事件模型:System.EventArgs,它是事件传输信息的基类。

3) Framework定义了一个泛型的代理:

public delegate void EventHandler<TEventArgs>(object source, TEventArgs e)

where TEventArgs : EventArgs;

定义好泛型代理的事件后,需要写一个受保护的虚方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: