c# 委托详解
委托是一个类型。C#中的委托是面向对象的,并且它是类型安全的当创建委托实例的时候,创建的实例会包含一个调用列表,在调用列表中可以包含多个方法。每个方法称作一个调用实体。调用实体可以是静态方法,也可以是实例方法。如果是实例方法,则该调用实体包含调用该实例方法的实例。委托并不关心它所调用方法所属的类,它只关心被调用方法与委托的类型是否兼容。下面是代码实例:
usingSystem; namespaceLycheeTest{ publicdelegatevoidD(inta,intb); publicclassTest{ publicDmyDelegate; publicTest(){ myDelegate=newD(Show1); } privatestaticvoidShow1(inta,intb){ Console.WriteLine("方法Show1被调用,两个实参相加的值是:{0}",a+b); } privatevoidShow2(inta,intb){ Console.WriteLine("方法Show2被调用,两个实参相加的值是:{0}",a+b); } privatevoidShow3(inta,intb){ Console.WriteLine("方法Show3被调用,两个实参相加的值是:{0}",a+b); } } publicclassProgram{ staticvoidMain(string[]args){ TestmyT=newTest(); myT.myDelegate(33,22); Console.ReadKey(); } } }
这段代码演示的是最简单的一种委托形式。委托类型可以定义在类的外部,也可以定义在类的内部。本段代码是定义在类的外部。第3行代码定义的就是一个委托类型,委托类型的关键字是delegate,关键字前是委托类型的访问权限修饰符。关键字后是委托类型的返回类型,这个返回类型规定与委托类型兼容的方法的返回类型必须与之相同。返回类型之后是委托类型的名称。接下来是形参列表,它指定与委托类型兼容的方法的参数类型和个数必须与之相同。第5行代码定义了一个委托类型的变量,它是一个实例字段,访问权限是public的。注意委托类型字段的访问权限一定要比委托类型的访问权限低或与委托类型的访问权限相同才可以。第9行、第12行和第15行代码定义了三个方法。其中第9行代码是一个静态方法。因为这段代码演示的是最简单的委托使用方法,所以只使用了其中的静态方法。在第6行的构造方法中,实例化了委托类型的变量,注意为委托变量的调用列表添加方法,只需要向其构造方法中传递方法名称即可。这是为委托添加调用方法的最基本的一种方法。第21行定义了Test类的一个实例,然后第22行调用了类的委托成员。在调用委托成员的时候,需要向其形参列表传递实参。这就是最基本的委托的使用方法。这段代码的执行结果如下:
方法Show1被调用,两个实参相加的值是:55
下面再介绍一种委托类型的使用方法,实例代码如下:
usingSystem; namespaceLycheeTest{ publicdelegatevoidD(inta,intb); publicclassTest{ publicstaticvoidShow1(inta,intb){ Console.WriteLine("方法Show1被调用,两个实参相加的值是:{0}",a+b); } publicvoidShow2(inta,intb){ Console.WriteLine("方法Show2被调用,两个实参相加的值是:{0}",a+b); } publicvoidShow3(inta,intb){ Console.WriteLine("方法Show3被调用,两个实参相加的值是:{0}",a+b); } } publicclassProgram{ staticvoidMain(string[]args){ TestmyT=newTest(); DmyDelegate=newD(Test.Show1); DmyDelegate1=newD(myT.Show2); DmyDelegate2=newD(myT.Show3); myDelegate(22,33); myDelegate1(33,44); myDelegate2(55,66); Console.ReadKey(); } } }
这段代码取消了类中的委托类型字段,而是将委托类型作为一个类来看待。在包含入口点方法的类中,首先第17行定义了Test类的一个变量并做了实例化。因为要向委托传递类的实例方法,所以必须有类的实例存在,才能引用类的实例方法。第18行定义了一个委托类型的变量,并实例化,这里需要注意,因为委托并不是类中的一个成员了,所以向其构造方法传递静态方法的时候,需要以类名引用。第19行也定义了一个委托类型的变量,在向其传递实例方法的时候,需要以类的实例来引用。第20行代码的情况同第19行代码一样。在向委托传递方法的时候,需要传递方法名,而不需要方法的形参列表。第21行到第23行是对委托的调用,这时要为其传递方法的实参。这段代码的执行结果如下:
方法Show1被调用,两个实参相加的值是:55 方法Show2被调用,两个实参相加的值是:77 方法Show3被调用,两个实参相加的值是:121
委托的访问修饰符
当委托位于类的外部时,可以使用的访问修饰符包括public和internal。如果什么也不写,默认是internal的。当委托位于类的内部时,可以使用的访问修饰符包括public、protected、internal、protected
usingSystem; namespaceLycheeTest{ publicclassTest{ protecteddelegatevoidD(inta,intb); privatedelegatevoidD1(inta,intb); protectedinternaldelegatevoidD2(inta,intb); internaldelegatevoidD3(inta,intb); privateDmyD; privateD1myD1; privateD2myD2; privateD3myD3; publicTest(){ myD=newD(Show1); myD1=newD1(Show1); myD2=newD2(Show1); myD3=newD3(Show1); } publicstaticvoidShow1(inta,intb){ Console.WriteLine("方法Show1被调用,两个实参相加的值是:{0}",a+b); } publicvoidShow2(inta,intb){ Console.WriteLine("方法Show2被调用,两个实参相加的值是:{0}",a+b); } publicvoidShow3(inta,intb){ Console.WriteLine("方法Show3被调用,两个实参相加的值是:{0}",a+b); } publicvoidUse(){ myD(11,12); myD1(22,45); myD2(55,78); myD3(345,100); } } classTest1:Test{ privateDTest1D; privateD2Test1D2; privateD3Test1D3; publicTest1(){ Test1D=newD(Test.Show1); Test1D2=newD2(Test.Show1); Test1D3=newD3(Test.Show1); } publicvoidUse1(){ Test1D(22,45); Test1D2(44,45); Test1D3(77,78); } } publicclassProgram{ staticvoidMain(string[]args){ Test1myT1=newTest1(); myT1.Use(); myT1.Use1(); Console.ReadKey(); } } }
代码的第4行在类的内部定义了委托类型,它作为类的成员定义,访问权限是protected,它可以被本类内部访问,也可以被派生类访问。代码的第5行定义的委托类型,访问权限是private的,它只可以被本类内部访问。代码的第6行定义的protectedinternal访问权限的委托类型,可以被本程序集访问,还可以被派生类访问,而不管派生类位于哪个程序集。第7行定义的委托类型是internal的,它只可以被本程序集访问。因为所有这几种委托类型都可以被本类内部访问,所以第10行到第13行定义了它们的变量。第12行的实例构造方法中,对这四个委托类型的变量进行了实例化,并为它们的调用列表加入了方法Show1。Show1是一个静态方法,但是在类内部传入委托类型的构造方法时,不需要使用类名引用。第27行定义了实例方法,在方法内部调用了这四个委托,并为其传入实参。第34行代码又定义了一个类,它继承自基类Test。因为基类中的委托类型只有D、D2和D3可以被派生类访问,所以第35行到第37行定义了它们的变量。注意,虽然它们和基类中的委托变量是同一种类型,但是它们是不同的委托。在第38行的实例构造方法中,为这三个委托类型的变量创建实例,并为其调用列表加入方法,因为静态方法Show1也被派生类所继承,所以这里传入的方法名,可以使用类名引用,也可以不使用类名引用。第43行定义了一个实例方法,方法内部调用了这三个委托,并为其传入实参。第51行定义了派生类的实例,然后调用实例方法Use和Use1。这段代码的执行结果如下:
方法Show1被调用,两个实参相加的值是:23 方法Show1被调用,两个实参相加的值是:67 方法Show1被调用,两个实参相加的值是:133 方法Show1被调用,两个实参相加的值是:445 方法Show1被调用,两个实参相加的值是:67 方法Show1被调用,两个实参相加的值是:89 方法Show1被调用,两个实参相加的值是:155
因为D和D2的访问权限被定义成了protected和protectedinternal。所以下面来验证在其它程序集中是否可以访问它们。首先要将本段代码中的包含Main方法的类去掉,然后在它的项目属性中将它改变为类库。接下来新建一个控制台项目,并物理上引用这个类库。控制台项目的代码如下:
usingSystem; usingLycheeTest; namespaceLycheeTest1{ classProgram:Test{ privateDpD; privateD2pD2; publicProgram(){ pD=newD(Show1); pD2=newD2(Show1); } publicvoidUse3(){ pD(34,33); pD2(12,11); } staticvoidMain(string[]args){ Programp=newProgram(); p.Use3(); Console.ReadKey(); } } }
因为第3行代码的命名空间和类库的命名空间是两个独立的命名空间,它们的成员不位于同一个命名空间内。所以在一个命名空间内引用另一个命名空间的成员时,需要加上另一个命名空间的名称进行引用。为了代码编写的方便,第2行代码首先引用了类库的命名空间。第4行代码定义了一个类,它继承自基类Test。因为是派生类,所以对于委托类型D和D2都可以访问。第5行代码和第6行代码分别定义了D和D2的两个变量。第7行的实例构造方法对这两个变量进行了实例化,并为其传入方法Show1。因为Show1方法被继承了下来,所以这里不需要类名引用。第11行代码定义了一个实例方法,它的作用是调用这两个委托,并为其传入实参。第16行代码定义了本类的一个实例,并调用了实例方法Use3。这段代码的执行结果如下:
方法Show1被调用,两个实参相加的值是:67 方法Show1被调用,两个实参相加的值是:23
类Test中的委托类型D2和D3都具有internal权限,现在来验证一下,对于一个同一程序集中的非派生类是否可以访问它们。首先将类库更改回控制台项目,然后增加一个类,这个类对于Test类来说是独立的。它们之间只是位于一个程序集内,彼此没有继承关系。代码如下:
usingSystem; namespaceLycheeTest{ publicclassTest{ protecteddelegatevoidD(inta,intb); privatedelegatevoidD1(inta,intb); protectedinternaldelegatevoidD2(inta,intb); internaldelegatevoidD3(inta,intb); privateDmyD; privateD1myD1; privateD2myD2; privateD3myD3; publicTest(){ myD=newD(Show1); myD1=newD1(Show1); myD2=newD2(Show1); myD3=newD3(Show1); } publicstaticvoidShow1(inta,intb){ Console.WriteLine("方法Show1被调用,两个实参相加的值是:{0}",a+b); } publicvoidShow2(inta,intb){ Console.WriteLine("方法Show2被调用,两个实参相加的值是:{0}",a+b); } publicvoidShow3(inta,intb){ Console.WriteLine("方法Show3被调用,两个实参相加的值是:{0}",a+b); } publicvoidUse(){ myD(11,12); myD1(22,45); myD2(55,78); myD3(345,100); } } classTest1{ privateTest.D2tD2; privateTest.D3tD3; publicTest1(){ tD2=newTest.D2(Test.Show1); tD3=newTest.D3(Test.Show1); } publicvoidUse3(){ tD2(34,33); tD3(22,21); } } publicclassProgram{ staticvoidMain(string[]args){ Test1myT1=newTest1(); myT1.Use3(); Console.ReadKey(); } } }
这段代码中,原来的类Test没有进行修改。在第35行上,定义了一个类,它是一个相对于Test类来说独立的类。它们的关系仅限于同在一个程序集内。第36行代码和第37行代码定义了委托类型D2和D3的两个变量。这里需要注意,因为这两个类不是继承关系,所以要引用Test类中的这两个委托类型需要使用Test类的类名进行引用。第38行代码是实例构造方法,在构造方法中将委托实例化。实例化委托类型的时候,仍然需要使用类名引用委托类型名,传递的方法名也是如此。第行42定义了一个实例方法,它调用了委托,并为其传入了实参。第49行代码定义了类Test1的一个实例,然后第61行调用类的实例方法。这段代码的执行结果如下:
方法Show1被调用,两个实参相加的值是:67 方法Show1被调用,两个实参相加的值是:43
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持毛票票!