时间:2016-11-4来源:本站原创作者:佚名
1.委托

委托类似于C++中的函数指针(一个指向内存位置的指针)。委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针。简单理解,委托是一种可以把函数当做参数传递的类型。很多情况下,某个函数需要动态地去调用某一类函数,这时候我们就在参数列表放一个委托当做函数的占位符。在某些场景下,使用委托来调用方法能达到减少代码量,实现某种功能的用途。

1.1自定义委托

声明和执行一个自定义委托,大致可以通过如下步骤完成:

利用关键字delegate声明一个委托类型,它必须具有和你想要传递的方法具有相同的参数和返回值类型;

创建委托对象,并且将你想要传递的方法作为参数传递给委托对象;

通过上面创建的委托对象来实现该委托绑定方法的调用。

下面一段代码,完成了一次应用委托的演示:

//step01:使用delegate关键字声明委托

publicdelegateintCalcSumDelegate(inta,intb);classProgram

{

staticvoidMain(string[]args)

{

//step03:实例化这个委托,并引用方法

CalcSumDelegatedel=newCalcSumDelegate(CalcSum);

//step04:调用委托

intresult=del(5,5);

Console.WriteLine(5+5=+result);

}//step02:声明一个方法和委托类型对应

publicstaticintCalcSum(inta,intb)

{

returna+b;

}

}

通过上面4个步骤,完成了委托从声明到调用的过程。接着,咱也学着大神用ILSpy反编译上面的代码生成的程序集。截图如下:

(1)自定义委托继承关系是:System.MulticastDelegate—System.Delegate—System.Object。

(2)委托类型自动生成3个方法:BeginInvoke、EndInvoke、Invoke。查资料得知,委托正是通过这3个方法在内部实现调用的。

Invoke方法,允许委托同步调用。上面调用委托的代码del(5,5)执行时,编译器会自动调用del.Invoke(5,5);

BeginInvoke方法,以允许委托的异步调用。假如上述委托以异步的方式执行,则要显示调用dal.BeginInvoke(5,5)。

注意:BeginInvoke和EndInvoke是.Net中使用异步方式调用同步方法的两个重要方法,具体用法详见微软官方示例。

1.2多播委托

一个委托可以引用多个方法,包含多个方法的委托就叫多播委托。下面通过一个示例来了解什么是多播委托:

//step01:声明委托类型

publicdelegatevoidPrintDelegate();

publicclassProgram

{

publicstaticvoidMain(string[]args)

{

//step03:实例化委托,并绑定第1个方法

PrintDelegatedel=Func1;

//绑定第2个方法

del+=Func2;

//绑定第3个方法

del+=Func3;

//step04:调用委托

del();//控制台输出结果:

//调用第1个方法!

//调用第2个方法!

//调用第3个方法!

}

//step02:声明和委托对应签名的3个方法

publicstaticvoidFunc1()

{

Console.WriteLine(调用第1个方法!);

}

publicstaticvoidFunc2()

{

Console.WriteLine(调用第2个方法!);

}

publicstaticvoidFunc3()

{

Console.WriteLine(调用第3个方法!);

}

}

可以看出,多播委托的声明过程是和自定义委托一样的,可以理解为,多播委托就是自定义委托在实例化时通过“+=”符号多绑定了两个方法。

(1)为什么能给委托绑定多个方法呢?

自定义委托的基类就是多播委托MulticastDelegate,这就要看看微软是如何对System.MulticastDelegate定义的:

MulticastDelegate拥有一个带有链接的委托列表,该列表称为调用列表,它包含一个或多个元素。在调用多路广播委托时,将按照调用列表中的委托出现的顺序来同步调用这些委托。如果在该列表的执行过程中发生错误,则会引发异常。(摘自MSDN)

(2)为什么使用“+=”号就能实现绑定呢?

先来看上述程序集反编译后的调用委托的代码:

“+=”的本质是调用了Delegate.Combine方法,该方法将两个委托连接在一起,并返回合并后的委托对象。

(3)多播委托能引用多个具有返回值的方法嘛?

答案是,当然能。委托的方法可以是无返回值的,也可以是有返回值的。不过,对于有返回值的方法需要我们从委托列表上手动调用。否则,就只能得到委托调用的最后一个方法的结果。下面通过两段代码验证下:

publicdelegatestringGetStrDelegate();

publicclassProgram

{

publicstaticvoidMain(string[]args)

{

GetStrDelegatedel=Func1;

del+=Func2;

del+=Func3;

stringresult=del();

Console.WriteLine(result);

//控制台输出结果:

//YoucalledmefromFunc3

}

publicstaticstringFunc1()

{

returnYoucalledmefromFunc1!;

}

publicstaticstringFunc2()

{

returnYoucalledmefromFunc2!;

}

publicstaticstringFunc3()

{

returnYoucalledmefromFunc3!;

}

}

正确做法:利用GetInvocationList获得委托列表上所有方法,循环依次执行委托,并处理委托返回值。

publicdelegatestringGetStrDelegate();

publicclassProgram

{

publicstaticvoidMain(string[]args)

{

GetStrDelegatedel=Func1;

del+=Func2;

del+=Func3;

//获取委托链上所有方法

Delegate[]delList=del.GetInvocationList();

//遍历,分别处理每个方法的返回值

foreach(GetStrDelegateitemindelList)

{

//执行当前委托

stringresult=item();

Console.WriteLine(result);

//控制台输出结果:

//YoucalledmefromFunc1

//YoucalledmefromFunc2

//YoucalledmefromFunc3

}

Console.ReadKey();

}

publicstaticstringFunc1()

{

returnYoucalledmefromFunc1;

}

publicstaticstringFunc2()

{

returnYoucalledmefromFunc2;

}

publicstaticstringFunc3()

{

returnYoucalledmefromFunc3;

}

}

1.3匿名方法

匿名方法是C#2.0版本引入的一个新特性,用来简化委托的声明。假如委托引用的方法只使用一次,那么就没有必要声明这个方法,这时用匿名方法表示即可。

//step01:定义委托类型

publicdelegatestringProStrDelegate(stringstr);

publicclassProgram

{

publicstaticvoidMain(string[]args)

{

//step02:将匿名方法指定给委托对象

ProStrDelegatedel=delegate(stringstr){returnstr.ToUpper();};

stringresult=del(KaSlFkaDhkjHe);

Console.WriteLine(result);

Console.ReadKey();

//输出:KASLFKAFHKJHE

}

}

(1)匿名方法只是C#提供的一个语法糖,方便开发人员使用。在性能上与命名方法几乎无异。

(2)匿名方法通常在下面情况下使用:

委托需要指定一个临时方法,该方法使用次数极少;

这个方法的代码很短,甚至可能比方法声明都短的情况下使用。

1.4Lambda表达式

Lambda表达式是C#3.0版本引入的一个新特性,它提供了完成和匿名方法相同目标的更加简洁的格式。下面示例用Lambda表达式简化上述匿名方法的例子:

publicdelegatestringProStrDelegate(stringstr);

publicclassProgram

{

publicstaticvoidMain(string[]args)

{

//匿名委托

//ProStrDelegatedel=delegate(stringstr){returnstr.ToUpper();};

//简化1

//ProStrDelegatedel1=(stringstr)={returnstr.ToUpper();};

//简化2

//ProStrDelegatedel2=(str)={returnstr.ToUpper();};

//简化3

ProStrDelegatedel3=str=str.ToUpper();

stringresult=del3(KaSlFkaDhkjHe);

Console.WriteLine(result);

Console.ReadKey();

//输出:KASLFKAFHKJHE

}

}

简化1:去掉delegate关键字,用”=”符号表示参数列表和方法体之间的关系;

简化2:去掉方法的参数类型;假如只有一个参数,参数列表小括号()也可省略;

简化3:如果方法体中的代码块只有一行,可以去掉return,去掉方法体的大括号{}。

1.5内置委托

上述几种委托的使用,都没能离开定义委托类型这一步骤。微软干脆直接把定义委托这一步骤封装好,形成三个泛型类:Action、Func和Predicate,这样就省去了定义的步骤,推荐使用。

publicclassProgram

{

publicstaticvoidMain(string[]args)

{

//Action

Actionstringaction=delegate(stringstr){Console.WriteLine(你好!+str);};

action(GG);

//Func

Funcint,int,intfunc=delegate(intx,inty){returnx+y;};

Console.WriteLine(计算结果:+func(5,6));

//Predicate

Predicateboolper=delegate(boolisTrue){returnisTrue==true;};

Console.WriteLine(per(true));

}

}

它们的区别如下:

Action委托:允许封装的方法有多个参数,不能有返回值;

Func委托:允许封装的方法有多个参数,必须有返回值;

Predicate委托:允许封装的方法有一个参数,返回值必须为bool类型。









































专业治白癜风医院
白癜风的治疗方法

转载请注明原文网址:http://www.helimiaopu.com/cxfz/cxfz/1286.html
------分隔线----------------------------