第十三章 匿名方法

  匿名方法(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;
        }
    }
View Code

  通过类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)对数(75
函数值: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());
        }
    }
View Code

  程序输出结果:

原始内容
姓名:李明
性别:女
公司:
职务:
办公电话:
商务电话:
住宅电话:
手机:
提示:将把当前联系人 姓名 由 李明 改为 李小明!
确认修改(Y/N)?y
提示:将把当前联系人 性别 由 女 改为 男!
确认修改(Y/N)?y
提示:将把当前联系人 公司 由  改为 微城公司!
确认修改(Y/N)?y
提示:将把当前联系人 头衔 由  改为 项目经理!
确认修改(Y/N)?y
当前内容
姓名:李小明
性别:男
公司:微城公司
职务:项目经理
办公电话:
商务电话:
住宅电话:
手机:
请按任意键继续. . .

7 小结

  C#支持将一段代码直接封装在一个代表对象中,而不是显式的定义在一个方法中,这就是匿名方法。匿名方法可以通过代表对象直接调用或动态调用。

  多个匿名方法之间可以实现状态共享,这是通过外部变量来实现的。匿名方法所带来的最大的优越性是可以把一段代码直接作为参数使用,而无需显式的定义一个方法,这样不仅减少了编程的工作量,提高了代码的可维护性,更极大地方便了程序中的各种计算。

原文地址:https://www.cnblogs.com/boywg/p/4149965.html