匿名方法(Anonymous Method)是指跳过方法的定义,而将方法的执行代码直接封装在一个代表对象中。除了简化代码之外,匿名方法的主要用途还包括多个方法间的状态共享,以及将代码段作为参数传递。
1 方法的命名调用和匿名调用
先看一个程序例子:
class NamedMethodSample { static void Main() { RW c1 = new RW(); while (c1.dg("密码") != "2004") { Console.WriteLine("密码错误!"); continue; } Console.WriteLine("通过验证"); } } public delegate string ConsoleDelegate(string str); public class RW { //字段 public ConsoleDelegate dg; //构造函数 public RW() { dg = new ConsoleDelegate(Read); } //方法 public string Read(string sPrompt) { Console.Write("请输入{0}:", sPrompt); return Console.ReadLine(); } }
程序中定义了一个名为ConsoleDelegate的代表类型,它有一个string类型的参数,且返回值也为string类型。在类RW中定义了一个ConsoleDelegate代表类型的字段dg,以及参数和返回类型与该代表一致的方法Read。在类的构造函数中,代表对象被创建,并与该方法相关联。程序主方法创建了一个类RW的实例,并通过其代表字段来调用该方法,模拟一个密码验证的过程。
上面的例子中,Read方法被明确的封装到代表对象dg之中,这种通过代表调用方法的方式称为命名方法调用。
另一种调用方式是匿名方法调用。如果一个方法只通过代表进行调用,C#中允许不写出该方法的定义,而是将方法的执行代码转移到代表的实例化过程中,看下面的程序:
public delegate string ConsoleDelegate(string str); public class RW { //字段 public ConsoleDelegate dg; //构造函数 public RW() { dg = delegate(string str) { Console.Write("请输入{0}:", str ); return Console.ReadLine(); }; } }
输出结果与第一个例子相同。
2 深入了解Delegate类
C#中使用关键字delegate定义的所有代表类型实际上都是System.Delegate的派生类。正是该类封装了代表所有基本功能的实现方式。
2.1 创建代表对象
Delegate类本身是一个抽象类,不能创建实例。使用delegate关键字定义的代表类型实际上都属于Delegate类的非抽象派生类,其实例的创建方式有3种方式。
第一种方式和普通对象类似,即使用关键字new来创建代表对象,并指定所封装的命名方法,如:
ConsoleDelegate dg = new ConsoleDelegate(Read);
这时要求所封装方法的参数和返回类型都要和代表中的定义相一致,否则创建就会失败。
第二种方式则是针对匿名方法,使用关键字delegate来创建代表对象,其后的参数列表及执行代码的返回类型也应和代表的定义保持一致:
ConsoleDelegate dg = delegate(string str) { Console.Write("请输入{0}:", str ); return Console.ReadLine(); };
第三种方式很少使用,它是通过Delegate类提供的静态方法CreateDelegate来创建代表对象,通常是将代表所要封装的方法信息及方法所属的类型信息作为参数传递给该方法,如:
ConsoleDelegate dg = (ConsoleDelegate)Delegate.CreateDelegate(typeof(ConsoleDelegate), c1, "AddMethod");
2.2 属性
Delegate类提供了两个公有属性:Method和Target,分别表示所封装的方法以及调用方法的对象,它们都只有get访问函数。在一个代表对象被实例化之后,这两个属性的值就已经确定了。根据所封装方法的类型和方式不同,这两个属性的取值可以分为以下几种情况:
(1)对于命名方法。其Method属性就是实例化代表对象时指定的方法。当方法为实例方法时,Target属性值为调用方法的当前对象;当方法为静态方法时,Target属性值为null。
(2)对于匿名方法,其Method属性就是赋值给代表对象的匿名方法表达式所创建的方法;其Target属性值始终为null。
下面的程序中使用了4个代表对象,其中的3个定义为类Class1的公有字段,而另一个则在类DelegateInfo的主方法中定义。代表对象dg1和dg3的实例化放在类Class1的构造函数中,而dg2和dg4则在程序主方法中进行实例化。dg1和dg2封装的都是命名方法,且一个为例方法,另一个为静态方法;而dg3和dg4封装的都是匿名方法。
class DelegateInfo { static void Main() { Class1 c1 = new Class1(); Console.WriteLine("dg1:"); DelegateInfo.WriteDelegateInfo(c1.dg1); c1.dg2 = new ADelegate(Class1.StaticMethod); Console.WriteLine("dg2:"); DelegateInfo.WriteDelegateInfo(c1.dg2); Console.WriteLine("dg3:"); DelegateInfo.WriteDelegateInfo(c1.dg3); ADelegate dg4 = delegate(int x) { return ++x; }; Console.WriteLine("dg4:"); DelegateInfo.WriteDelegateInfo(dg4); } public static void WriteDelegateInfo(Delegate dg) { if (dg == null) return; Console.WriteLine("MethodName:{0}", dg.Method.Name); Console.WriteLine("MethodBelongType:{0}", dg.Method.DeclaringType); Console.WriteLine("Target:{0}", dg.Target); Console.WriteLine(); } } public delegate int ADelegate(int x); public class Class1 { //字段 public ADelegate dg1; public ADelegate dg2; public ADelegate dg3; //构造函数 public Class1() { dg1 = new ADelegate(InstanceMethod); dg3 = delegate(int x) { return ++x; }; } //方法 public int InstanceMethod(int x) { return ++x; } //静态方法 public static int StaticMethod(int x) { return ++x; } }
通过类DelegateInfo所提供的WriteDelegateInfo,程序输出了这4个代表对象各自的Method和Target属性信息:
dg1: MethodName:InstanceMethod MethodBelongType:P15_3.Class1 Target:P15_3.Class1 dg2: MethodName:StaticMethod MethodBelongType:P15_3.Class1 Target: dg3: MethodName:<.ctor>b__0 MethodBelongType:P15_3.Class1 Target: dg4: MethodName:<Main>b__0 MethodBelongType:P15_3.DelegateInfo Target: 请按任意键继续. . .
如果两个代表对象合并为一个新的代表对象,那么新代表对象的Method和Target属性与合并后的代表对象相同。例如,可以合并上例中的两个代表对象,那么dg5的属性与c1.dg2的相同:
ADelegate dg5 = c1.dg1 + c1.dg2;
2.3 方法调用
通过代表对象来调用其封装方法有两种方式。
一是直接调用,即把参数直接传递给代表对象,这时代表看上去更像是一个方法。通过代表对象直接调用方法时,要求所传递的参数型与方法定义的参数类型相同,或者是能够隐式转换为方法定义的参数类型。
另一种方式叫做动态调用,它是通过Delegate类所定义的DynamicInvoke方法进行,传递给该方法的参数是一个数组,其类型为object[],而返回类型为object。也就是说可以将一组对象(甚至是空值)传递给代表对象,代码总是能够通过编译,而一致性检查和类型转换的工作都要等到运行时才进行。这种调用方式更为灵活,但带来的开销也更大。
例如下面的程序运行后,将直接输出用户为程序指定的两个参数;而如果指定的参数不是两个的话就会发生错误:
class DynamicInvokeSample { static void Main(string[] args) { RW rw1 = new RW(); ReadDelegate dg1 = new ReadDelegate(rw1.Read); WriteDelegate dg2 = new WriteDelegate(rw1.Write); dg2.DynamicInvoke(args); dg2.DynamicInvoke(new string[] { args[0], dg1.DynamicInvoke(args[0]).ToString() }); } } public delegate string ReadDelegate(string s1); public delegate void WriteDelegate(string s1,string s2); public class RW { //方法 public string Read(string sPrompt) { Console.Write("请输入{0}:", sPrompt); return Console.ReadLine(); } public void Write(string sPrompt, string sContent) { Console.WriteLine("目前{0}为:{1}", sPrompt, sContent); } }
程序输出结果:
P15_4 密码 2004 目前密码为:2004 请输入密码:123456 目前密码为:123456
Delegate类还提供了一个GetInvokationList方法,该方法返回一个Delegate[]数组,表示代表的调用列表。对于直接定义的代表对象,该方法返回数组中只有对象本身的一个元素;对于合并后的代表对象,它所包含的各个代表对象共同组成了返回的数组。
3 匿名方法的定义规则
匿名方法表达式由3部分组成:关键字delegate、参数列表以及执行代码段、其中只有参数列表部分可以省略。
以斌值给某个代表对象时,要求匿名方法表达式与代表的定义保持一致。体现在对参数列表和返回类型的要求上:
• 匿名方法表达式的返回类型和代表定义的返回类型相同,或是可以隐式转换为代表的返回类型。这里所说的匿名方法表达式的返回类型,是指其执行代码段中return语句的返回类型。如果存在多个return语句,则要求每个语句的返回类型相同,并与代表的返回类型一致。如果代表定义的返回类型为void,那么执行代码段中要么没有return语句,要么只有不跟任何表达式的单独return语句。
• 匿名方法表达式可以省略参数列表,此时它所对应的代表类型可以有任意个参数,但不能包含输出参数,而且不能在代码执行段中使用这些地参数。
• 如果匿名方法表达式指定了参数列表,则参数数量要与代表中所定义的相同,且每个参数的类型应和代表中对应的参数类型相同,或是可以隐式转换为代表中相对应的参数类型。
• 匿名方法表达式的参数列表中不能有关键字params所修饰的数组型参数。
例如对于下面的代表定义:
public delegate void ZeroDelegate(); public delegate int OneDelegate(int x); public delegate void TwoDelegate(ref int x, ref int y); public delegate bool MoreDelegate(out double x, double[] y);
下面的语句都是合法的:
ZeroDelegate dg0 = delegate() { Console.WriteLine("Hello"); }; OneDelegate dg1 = delegate(int x) { return ++x; }; TwoDelegate dg2 = delegate(ref int x, ref int y) { int tmp = x; x = y; y = tmp; }; dg2 = delegate(ref int x, ref int y) { x = y; return; }; MoreDelegate dg3 = delegate(out double x, double[] y) { x = y[0]; return y.Length > 0; };
而下面的语句都是不合法的:
//返回类型不一致 ZeroDelegate dg0 = delegate() { return 10; }; //参数个数不一致 OneDelegate dg1 = delegate() { return 10; }; //参数类型不一致 TwoDelegate dg2 = delegate(int x, int y) { int tmp = x; x = y; y = tmp; }; //对应的代表含有输出参数,参数列表不能省略 MoreDelegate dg3 = delegate {return false };
此外,匿名方法表达式中出现的参数名称不能和其所在代码段中其它变量或常量的名称相同,如下面的代码是错误的,因为x已经作为另一个局部变量的名称:
int x = 10; OneDelegate dg1 = delegate(int x){return ++x;);
4 外部变量
匿名方法的作用不仅仅是简化代码,它还提供了一种有效的手段,用于在不同的方法成员 之间共享某个局部状态。实现这种共享的关键就在于外部变量。
匿名方法的外部变量是指匿名方法表达式所在代码中出现的所有变量。当外部变量作为参数传递给匿名方法时,其使用规则和普通方法的参数使用规则完全相同,如引用参数和值参数必须是已初始化的,外部参数可以未初始化,但必须在方法返回之前被赋值。
除了作为参数使用外,C#语言还允许在匿名方法表达式的执行代码中直接使用外部变量,这和命名方法是不同的。这时称外部变量被匿名方法“捕获”,而代码段中对变量所作的修改将生效,看下面的程序:
class OuterVariableSample { public delegate int ADelegate(int x); static void Main() { int x = 0; ADelegate dg = new ADelegate(AddMethod); Console.WriteLine(dg(x)); Console.WriteLine(x); Console.WriteLine(dg(x)); Console.WriteLine(x); dg = delegate { return ++x; }; Console.WriteLine(dg(x)); Console.WriteLine(x); Console.WriteLine(dg(x)); Console.WriteLine(x); } public static int AddMethod(int x) { return ++x; } }
当代表对象封装的是命名方法时,变量x只是作为形参以值传递的方式传递给方法;而当dg封装的是匿名方法时,x就是匿名方法的外部变量,由于该变量在匿名方法的执行代码中出现,它就被“捕获”了。程序输出结果为:
1 0 1 0 1 1 2 2 请按任意键继续. . .
由于被“捕获”后外部变量是被匿名方法直接引用的,所以未赋值的外部变量既不能在匿名方法表达式的执行代码段中使用,也不能作为输出参数以外的其它参数传递给匿名方法。下面的代码就是不合法的:
int x; ADelegate dg1 = delegate{return ++x;};//error:x未被赋值 dg1(5);
如果外部变量未被赋值,即使它被匿名方法赋值,方法返回之后它仍然处于未被赋值的状态。下面的代码同样是不合法的:
int x; ADelegate dg1 = delegate{x = 10;return ++x;}; dg1(5); Console.WriteLine(x);//error:x未被赋值
利用外部变量能够被“捕获”的特性,多个匿名方法之间就可以通过外部变量来实现状态共享和消息传递。下面的程序用于近似计算0˜90度之间的正弦和余弦函数表,并与系统提供的计算方法进行了比较:
public delegate double TriangleDelegate(double x); static void Main() { const double sinone = Math.PI / 180; double sin = sinone; double cos = Math.Sqrt(1 - sinone * sinone); TriangleDelegate dSin = delegate { return (sin + (sinone * cos)) < 1 ? sin += (sinone * cos) : sin = 1; }; TriangleDelegate dCos = delegate { return cos = Math.Sqrt(1 - sin * sin); }; for (int angle = 1; angle <= 90; angle++) { Console.WriteLine("Sin{0} = {1} , {2}", angle, sin, Math.Sin(Math.PI * angle / 180)); Console.WriteLine("Cos{0} = {1} , {2}", angle, cos, Math.Cos(Math.PI * angle / 180)); dSin(sin); dCos(cos); } }
程序输出结果:
Sin1 = 0.0174532925199433 , 0.0174524064372835 Cos1 = 0.999847679689368 , 0.999847695156391 Sin2 = 0.0349039265489484 , 0.034899496702501 Cos2 = 0.999390672315619 , 0.999390827019096 Sin3 = 0.0523465842945757 , 0.0523359562429438 Cos3 = 0.998628977705279 , 0.998629534754574 Sin4 = 0.0697759479613579 , 0.0697564737441253 Cos4 = 0.997562688298883 , 0.997564050259824 Sin5 = 0.0871867013672193 , 0.0871557427476582 Cos5 = 0.996191989078764 , 0.996194698091746 Sin6 = 0.104573531558635 , 0.104528463267653 Cos6 = 0.99451715746756 , 0.994521895368273 Sin7 = 0.121931130424019 , 0.121869343405147 Cos7 = 0.992538563197179 , 0.992546151641322 Sin8 = 0.139254196304824 , 0.139173100960065 Cos8 = 0.990256668147959 , 0.99026806874157 Sin9 = 0.156537435603834 , 0.156434465040231 Cos9 = 0.98767202615776 , 0.987688340595138 Sin10 = 0.173775564390131 , 0.17364817766693 Cos10 = 0.984785282800719 , 0.984807753012208 Sin11 = 0.190963310000187 , 0.190808995376545 Cos11 = 0.981597175135387 , 0.981627183447664 Sin12 = 0.208095412634575 , 0.207911690817759 Cos12 = 0.978108531421972 , 0.978147600733806 Sin13 = 0.225166626949735 , 0.224951054343865 Cos13 = 0.974320270808362 , 0.974370064785235 Sin14 = 0.242171723644263 , 0.241921895599668 Cos14 = 0.970233402984646 , 0.970295726275996 Sin15 = 0.259105491039174 , 0.258819045102521 Cos15 = 0.965849027805769 , 0.965925826289068 Sin16 = 0.275962736651571 , 0.275637355816999 Cos16 = 0.961168334881968 , 0.961261695938319 Sin17 = 0.292738288761173 , 0.292371704722737 Cos17 = 0.956192603136617 , 0.956304755963035 Sin18 = 0.309426997969122 , 0.309016994374947 Cos18 = 0.950923200331034 , 0.951056516295154 Sin19 = 0.326023738748501 , 0.325568154457157 Cos19 = 0.945361582555823 , 0.945518575599317 Sin20 = 0.342523410985964 , 0.342020143325669 Cos20 = 0.939509293688221 , 0.939692620785908 Sin21 = 0.35892094151391 , 0.3583679495453 Cos21 = 0.933367964814932 , 0.933580426497202 Sin22 = 0.375211285632569 , 0.374606593415912 Cos22 = 0.926939313619805 , 0.927183854566787 Sin23 = 0.391389428621411 , 0.390731128489274 Cos23 = 0.920225143735709 , 0.92050485345244 Sin24 = 0.407450387239237 , 0.4067366430758 Cos24 = 0.913227344059844 , 0.913545457642601 Sin25 = 0.423389211212324 , 0.422618261740699 Cos25 = 0.905947888031649 , 0.90630778703665 Sin26 = 0.439200984709966 , 0.438371146789077 Cos26 = 0.89838883287238 , 0.898794046299167 Sin27 = 0.454880827806738 , 0.453990499739547 Cos27 = 0.89055231878529 , 0.891006524188368 Sin28 = 0.470423897930811 , 0.469471562785891 Cos28 = 0.882440568115259 , 0.882947592858927 Sin29 = 0.485825391297592 , 0.484809620246337 Cos29 = 0.874055884466515 , 0.874619707139396 Sin30 = 0.501080544327964 , 0.5 Cos30 = 0.865400651776963 , 0.866025403784439 Sin31 = 0.516184635050376 , 0.515038074910054 Cos31 = 0.856477333347421 , 0.857167300702112 Sin32 = 0.53113298448599 , 0.529919264233205 Cos32 = 0.847288470823842 , 0.848048096156426 Sin33 = 0.545920958016054 , 0.544639035015027 Cos33 = 0.83783668313033 , 0.838670567945424 Sin34 = 0.560543966730667 , 0.559192903470747 Cos34 = 0.828124665350483 , 0.829037572555042 Sin35 = 0.574997468758009 , 0.573576436351046 Cos35 = 0.818155187554221 , 0.819152044288992 Sin36 = 0.589276970573102 , 0.587785252292473 Cos36 = 0.807931093566888 , 0.809016994374947 Sin37 = 0.603378028285082 , 0.601815023152048 Cos37 = 0.79745529967692 , 0.798635510047293 Sin38 = 0.617296248901923 , 0.615661475325658 Cos38 = 0.786730793277863 , 0.788010753606722 Sin39 = 0.631027291571448 , 0.629320391049837 Cos39 = 0.775760631439881 , 0.777145961456971 Sin40 = 0.644566868797424 , 0.642787609686539 Cos40 = 0.764547939405165 , 0.766044443118978 Sin41 = 0.657910747629383 , 0.656059028990507 Cos41 = 0.753095909000804 , 0.754709580222772 Sin42 = 0.671054750824746 , 0.669130606358858 Cos42 = 0.741407796961657 , 0.743144825477394 Sin43 = 0.683994757981685 , 0.681998360062498 Cos43 = 0.729486923154608 , 0.731353701619171 Sin44 = 0.696726706640975 , 0.694658370458997 Cos44 = 0.71733666869415 , 0.719339800338651 Sin45 = 0.709246593354976 , 0.707106781186547 Cos45 = 0.704960473937625 , 0.707106781186548 Sin46 = 0.721550474721607 , 0.719339800338651 Cos46 = 0.692361836346446 , 0.694658370458997 Sin47 = 0.733634468381007 , 0.73135370161917 Cos47 = 0.679544308197278 , 0.681998360062498 Sin48 = 0.745494753972237 , 0.743144825477394 Cos48 = 0.666511494124351 , 0.669130606358858 Sin49 = 0.757127574047093 , 0.754709580222772 Cos49 = 0.653267048470657 , 0.656059028990507 Sin50 = 0.768529234937692 , 0.766044443118978 Cos50 = 0.639814672421699 , 0.642787609686539 Sin51 = 0.779696107574019 , 0.777145961456971 Cos51 = 0.626158110890471 , 0.629320391049838 Sin52 = 0.790624628247126 , 0.788010753606722 Cos52 = 0.612301149116261 , 0.615661475325658 Sin53 = 0.801311299312949 , 0.798635510047293 Cos53 = 0.598247608932449 , 0.601815023152048 Sin54 = 0.811752689831004 , 0.809016994374947 Cos54 = 0.584001344649248 , 0.587785252292473 Sin55 = 0.821945436131208 , 0.819152044288992 Cos55 = 0.56956623848599 , 0.573576436351046 Sin56 = 0.831886242300987 , 0.829037572555042 Cos56 = 0.554946195473348 , 0.559192903470747 Sin57 = 0.841571880583413 , 0.838670567945424 Cos57 = 0.54014513772809 , 0.544639035015027 Sin58 = 0.850999191675407 , 0.848048096156426 Cos58 = 0.525166997980456 , 0.529919264233205 Sin59 = 0.86016508491298 , 0.857167300702112 Cos59 = 0.510015712205659 , 0.515038074910054 Sin60 = 0.869066538327873 , 0.866025403784439 Cos60 = 0.494695211174323 , 0.5 Sin61 = 0.877700598556713 , 0.874619707139396 Cos61 = 0.479209410689301 , 0.484809620246337 Sin62 = 0.886064380579783 , 0.882947592858927 Cos62 = 0.463562200214561 , 0.469471562785891 Sin63 = 0.894155067261316 , 0.891006524188368 Cos63 = 0.447757429520617 , 0.453990499739547 Sin64 = 0.901969908656718 , 0.898794046299167 Cos64 = 0.431798892863092 , 0.438371146789077 Sin65 = 0.909506221043645 , 0.90630778703665 Cos65 = 0.41569031006617 , 0.422618261740699 Sin66 = 0.916761385622936 , 0.913545457642601 Cos66 = 0.399435303685985 , 0.4067366430758 Sin67 = 0.923732846820959 , 0.92050485345244 Cos67 = 0.383037371158541 , 0.390731128489274 Sin68 = 0.93041811010586 , 0.927183854566787 Cos68 = 0.366499850459779 , 0.374606593415912 Sin69 = 0.93681473920445 , 0.933580426497202 Cos69 = 0.34982587727225 , 0.3583679495453 Sin70 = 0.942920352571528 , 0.939692620785908 Cos70 = 0.333018330886432 , 0.342020143325669 Sin71 = 0.948732618914992 , 0.945518575599317 Cos71 = 0.31607976494344 , 0.325568154457157 Sin72 = 0.954249251512185 , 0.951056516295154 Cos72 = 0.299012317452701 , 0.309016994374947 Sin73 = 0.959468000955753 , 0.956304755963035 Cos73 = 0.281817591966809 , 0.292371704722737 Sin74 = 0.964386645825616 , 0.961261695938319 Cos74 = 0.264496497808985 , 0.275637355816999 Sin75 = 0.969002980572377 , 0.965925826289068 Cos75 = 0.247049030845803 , 0.258819045102521 Sin76 = 0.973314799574497 , 0.970295726275996 Cos76 = 0.229473965689481 , 0.241921895599668 Sin77 = 0.977319875823387 , 0.974370064785235 Cos77 = 0.211768411998957 , 0.224951054343865 Sin78 = 0.981015931864488 , 0.978147600733806 Cos78 = 0.193927154952702 , 0.207911690817759 Sin79 = 0.984400599227438 , 0.981627183447664 Cos79 = 0.175941638734725 , 0.190808995376545 Sin80 = 0.987471360114714 , 0.984807753012208 Cos80 = 0.157798330007632 , 0.17364817766693 Sin81 = 0.990225460527495 , 0.987688340595138 Cos81 = 0.139475938151029 , 0.156434465040231 Sin82 = 0.992659774875539 , 0.99026806874157 Cos82 = 0.120940362758033 , 0.139173100960066 Sin83 = 0.994770582404223 , 0.992546151641322 Cos83 = 0.102134658090011 , 0.121869343405147 Sin84 = 0.996553168468292 , 0.994521895368273 Cos84 = 0.0829565091828715 , 0.104528463267653 Sin85 = 0.998001032689494 , 0.996194698091746 Cos85 = 0.0631976166536618 , 0.0871557427476581 Sin86 = 0.999104039179514 , 0.997564050259824 Cos86 = 0.0423216126250015 , 0.0697564737441255 Sin87 = 0.999842690664574 , 0.998629534754574 Cos87 = 0.0177367957823737 , 0.052335956242944 Sin88 = 1 , 0.999390827019096 Cos88 = 0 , 0.0348994967025011 Sin89 = 1 , 0.999847695156391 Cos89 = 0 , 0.0174524064372834 Sin90 = 1 , 1 Cos90 = 0 , 6.12303176911189E-17 请按任意键继续. . .
5 代表对象作为方法参数和返回值
代表本身是一种数据类型,因此代表对象可以作为变量使用。但代表对象的特殊性在于它封装了方法,于是它既有变量的说明性,又有方法的过程性。如果将代表变量作为参数传递给其它方法,或者由方法返回一个代表变量,这就能够实现以往程序设计语言特别是过程化设计语言很难实现的一个功能:将过程(子程序)作为参数和返回值进行传递。这种功能的实现会为各种科学和工程计算带来巨大的高效性和灵活性。
下向的程序定义了一个PrimaryFunction类,其中封装了多个初等数学函数的计算过程,包括11个一元函数和8个二元函数。这些函数的计算过程都是通过匿名方法来实现 的,程序在主方法中利用控制台模拟了一个科学计算器的功能:
class FunctionParameterSample { static void Main() { Console.WriteLine("请选择函数类型:1.一元函数 2.二元函数(按其它任意键退出)"); ConsoleKeyInfo key = Console.ReadKey(); if (key.KeyChar == '1') { Console.Write("请输入操作数:"); double x = double.Parse(Console.ReadLine()); Console.WriteLine("请选择函数:"); Console.WriteLine("平方根(0)e(1)ln(2)Sin(3)Cos(4)Tan(5)CTan(6)ArcSin(7)ArcCos(8)ArcTan(9)ArcCTan(10)"); int iType = int.Parse(Console.ReadLine()); OneFunction function = PrimaryFunction.GetOneFunction(iType); Console.WriteLine("函数值:{0}", function(x)); } else if (key.KeyChar == '2') { Console.Write("请输入操作数x:"); double x = double.Parse(Console.ReadLine()); Console.Write("请输入操作数y:"); double y = double.Parse(Console.ReadLine()); Console.WriteLine("请选择函数:"); Console.WriteLine("加(0)减(1)乘(2)除(3)求余(4)乘方(5)开方(6)对数(7)"); int iType = int.Parse(Console.ReadLine()); TwoFunction function = PrimaryFunction.GetTwoFunction(iType); Console.WriteLine("函数值:{0}", function(x, y)); } } } public delegate double OneFunction(double x); public delegate double TwoFunction(double x,double y); public class PrimaryFunction { public static OneFunction GetOneFunction(int iType) { switch (iType) { case 0: return delegate(double x) { return Math.Sqrt(x); }; case 1: return delegate(double x) { return Math.Exp(x); }; case 2: return delegate(double x) { return Math.Log(x); }; case 3: return delegate(double x) { return Math.Sin(x); }; case 4: return delegate(double x) { return Math.Cos(x); }; case 5: return delegate(double x) { return Math.Tan(x); }; case 6: return delegate(double x) { return 1 / Math.Tan(x); }; case 7: return delegate(double x) { return Math.Asin(x); }; case 8: return delegate(double x) { return Math.Acos(x); }; case 9: return delegate(double x) { return Math.Atan(x); }; case 10: return delegate(double x) { return Math.Atan(1 / x); }; default: return delegate(double x) { return x; }; } } public static TwoFunction GetTwoFunction(int iType) { switch (iType) { case 0: return delegate(double x, double y) { return x + y; }; case 1: return delegate(double x, double y) { return x - y; }; case 2: return delegate(double x, double y) { return x * y; }; case 3: return delegate(double x, double y) { return x / y; }; case 4: return delegate(double x, double y) { return x % y; }; case 5: return delegate(double x, double y) { return Math.Pow(x, y); }; case 6: return delegate(double x, double y) { return Math.Pow(x, 1 / y); }; case 7: return delegate(double x, double y) { return Math.Log(x, y); }; default: return delegate(double x, double y) { return x; }; } } }
程序演示计算6的6次方的过程,输出结果如下:
请选择函数类型:1.一元函数 2.二元函数(按其它任意键退出) 2请输入操作数x:6 请输入操作数y:6 请选择函数: 加(0)减(1)乘(2)除(3)求余(4)乘方(5)开方(6)对数(7) 5 函数值:46656 请按任意键继续. . .
下面的程序演示了打印常用的三角函数表:
class ArrayFunctionSample { static void Main() { Console.WriteLine("请选择函数类型:"); Console.WriteLine("1.正弦函数 2.余弦函数 3.正切函数 4.余切函数"); int iType = int.Parse(Console.ReadLine()); for (int i = 0; i < 10; i++) { Console.Write("{0,8}", 0.1 * i); } Console.WriteLine(); for (int i = 0; i < 90; i++) { Console.Write("{0,2}:", i); for (int j = 0; j < 10; j++) { Console.Write("{0,8}", TriangleTable.CreateFunctionTable(iType)[i][j].ToString("F4")); } Console.WriteLine(); } } } public delegate double OneFunction(double x); public class TriangleTable { public static double[] Apply(double[] target, OneFunction f) { double[] result = new double[target.Length]; for (int i = 0; i < target.Length; i++) result[i] = f(target[i]); return result; } public static OneFunction GetOneFunction(int iType) { switch (iType) { case 1: return delegate(double x) { return Math.Sin(x * Math.PI / 180); }; case 2: return delegate(double x) { return Math.Cos(x * Math.PI / 180); }; case 3: return delegate(double x) { return Math.Tan(x * Math.PI / 180); }; case 4: return delegate(double x) { return 1 / Math.Tan(x * Math.PI / 180); }; default: return delegate(double x) { return x; }; } } public static double[][] CreateFunctionTable(int iType) { OneFunction function = GetOneFunction(iType); if (function == null) return null; double[][] result = new double[90][]; double[] target = new double[10] { 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9 }; for (int i = 0; i < 90; i++) { result[i] = Apply(target, function); target = Apply(target, delegate(double x) { return x = x + 1; }); } return result; } }
程序中的TriangleTable类中的第一个静态方法Apply,它以一个数组对象和OneFunction类型的代表对象作为参数,在执行代码中将代表所封装的一元函数作用于数组的每个对象。通过将不同的代表对象传递该方法,就可以实现不同函数的批量求值。
6 在事件中使用匿名方法
对象可以通过事件的代表来调用事件处理代码,从而对特定的事件作出反映。而如果将匿名方法封装到事件的代表中,就可以在事件中同样享受匿名方法所带来的优越性。看下面的例子:
class AnonymousEventSample { static void Main() { ConsoleRW rw1 = new ConsoleRW(); Business c1 = new Business("李明"); Console.WriteLine("原始内容"); c1.Output<ConsoleRW>(rw1); c1.Name = "李小明"; c1.Gender = "男"; c1.Company = "微城公司"; c1.Title = "项目经理"; Console.WriteLine("当前内容"); c1.Output<ConsoleRW>(rw1); } } /// <summary> /// 基类:联系人Contact /// </summary> public class Contact:IComparable<Contact> { //字段 protected string m_name = "未知"; protected string m_gender = "女"; protected string[] m_phones; //属性 public string Name { get { return m_name; } set { ChangeEventArgs e = new ChangeEventArgs("姓名", m_name, value); eh(this, e); if(e.bChanged ) m_name = value; } } public string Gender { get { return m_gender; } set { if (value == "男" || value == "女") { ChangeEventArgs e = new ChangeEventArgs("性别", m_gender, value); eh(this, e); if (e.bChanged) m_gender = value; } } } //构造函数 public Contact() { m_phones = new string[3]; InfomationChange += delegate(object sender, EventArgs e) { Console.WriteLine("提示:将把当前联系人 {0} 由 {1} 改为 {2}!", ((ChangeEventArgs)e).InfoType, ((ChangeEventArgs)e).OldValue, ((ChangeEventArgs)e).NewValue); Console.Write("确认修改(Y/N)?"); char key = Console.ReadKey().KeyChar; Console.WriteLine(); if (key == 'Y' || key == 'y') ((ChangeEventArgs)e).bChanged = true; }; } public Contact(string sName) { m_name = sName; m_phones = new string[3]; InfomationChange += delegate(object sender, EventArgs e) { Console.WriteLine("提示:将把当前联系人 {0} 的{1}由 {2} 改为 {3}!", ((ChangeEventArgs)e).InfoType, m_name, ((ChangeEventArgs)e).OldValue, ((ChangeEventArgs)e).NewValue); Console.Write("确认修改(Y/N)?"); char key = Console.ReadKey().KeyChar; Console.WriteLine(); if (key == 'Y' || key == 'y') ((ChangeEventArgs)e).bChanged = true; }; } //代表 protected internal EventHandler eh; //事件 public event EventHandler InfomationChange { add { eh += (EventHandler)Delegate.Combine(eh, value); } remove { eh -= (EventHandler)Delegate.Combine(eh, value); } } //事件参数类 public class ChangeEventArgs : EventArgs { public string InfoType; public string OldValue; public string NewValue; public bool bChanged; public ChangeEventArgs(string sInfoType, string sOldValue, string sNewValue) { InfoType = sInfoType; OldValue = sOldValue; NewValue = sNewValue; } } //方法 public int CompareTo(Contact con) { return this.m_name.CompareTo(con.m_name); } public bool Equals(Contact con) { return this.Name.Equals(con.Name); } public virtual void Input<T>(T tp) where T : RW { m_name = tp.Read("姓名"); Gender = tp.Read("性别"); m_phones[0] = tp.Read("住宅电话"); m_phones[1] = tp.Read("办公电话"); m_phones[2] = tp.Read("手机"); } public virtual void Output<T>(T tp) where T : RW { tp.Write("姓名", m_name); tp.Write("性别", m_gender); tp.Write("住宅电话", m_phones[0]); tp.Write("办公电话", m_phones[1]); tp.Write("手机", m_phones[2]); } } /// <summary> /// 派生类:商务Business /// </summary> public class Business:Contact { //字段 protected string m_company = ""; protected string m_title = ""; //属性 public string Company { get { return m_company; } set { ChangeEventArgs e = new ChangeEventArgs("公司", m_company, value); eh(this, e); if(e.bChanged) m_company = value; } } public string Title { get { return m_title; } set { ChangeEventArgs e = new ChangeEventArgs("头衔", m_title, value); eh(this, e); if(e.bChanged ) m_title = value; } } //构造函数 public Business() { m_phones = new string[4]; } public Business(string sName) { m_name = sName; m_phones = new string[4]; } //重载方法 public override void Input<T>(T tp) { m_name = tp.Read("姓名"); Gender = tp.Read("性别"); m_company = tp.Read("公司"); m_title = tp.Read("职务"); m_phones[0] = tp.Read("办公电话"); m_phones[1] = tp.Read("商务电话"); m_phones[2] = tp.Read("住宅电话"); m_phones[3] = tp.Read("手机"); } public override void Output<T>(T tp) { tp.Write("姓名", m_name); tp.Write("性别", m_gender); tp.Write("公司", m_company); tp.Write("职务", m_title); tp.Write("办公电话", m_phones[0]); tp.Write("商务电话", m_phones[1]); tp.Write("住宅电话", m_phones[2]); tp.Write("手机", m_phones[3]); } } /// <summary> /// 派生类:同学Classmate /// </summary> public class Classmate:Contact { //字段 protected DateTime m_birthday; //属性 public DateTime Birthday { get { return m_birthday; } set { ChangeEventArgs e = new ChangeEventArgs("生日", m_birthday.ToShortDateString(), value.ToShortDateString()); eh(this, e); if (e.bChanged) m_birthday = value; } } //构造函数 public Classmate() : base() { } public Classmate(string sName) : base(sName) { } //方法 public override void Input<T>(T tp) { base.Input(tp); m_birthday = DateTime.Parse(tp.Read("生日(yyyy-mm-dd):")); } public override void Output<T>(T tp) { base.Output(tp); tp.Write("生日:", m_birthday.ToShortDateString()); } }
程序输出结果:
原始内容 姓名:李明 性别:女 公司: 职务: 办公电话: 商务电话: 住宅电话: 手机: 提示:将把当前联系人 姓名 由 李明 改为 李小明! 确认修改(Y/N)?y 提示:将把当前联系人 性别 由 女 改为 男! 确认修改(Y/N)?y 提示:将把当前联系人 公司 由 改为 微城公司! 确认修改(Y/N)?y 提示:将把当前联系人 头衔 由 改为 项目经理! 确认修改(Y/N)?y 当前内容 姓名:李小明 性别:男 公司:微城公司 职务:项目经理 办公电话: 商务电话: 住宅电话: 手机: 请按任意键继续. . .
7 小结
C#支持将一段代码直接封装在一个代表对象中,而不是显式的定义在一个方法中,这就是匿名方法。匿名方法可以通过代表对象直接调用或动态调用。
多个匿名方法之间可以实现状态共享,这是通过外部变量来实现的。匿名方法所带来的最大的优越性是可以把一段代码直接作为参数使用,而无需显式的定义一个方法,这样不仅减少了编程的工作量,提高了代码的可维护性,更极大地方便了程序中的各种计算。