• 第十三章 匿名方法


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

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

  • 相关阅读:
    Discuz 模板语句分析及知识技巧
    phpcms v9添加新模块
    simplexml 对xml的增删改操纵
    phpcms V9 相关阅读/相关文章
    怎么让php生成的网页源代码开头不出现空行
    phpcms v9 如何实现标签的嵌套
    在{pc:content action="lists"标签中加自定义限制条件的办法
    phpcms v9 内容页浏览数不显示问题
    【今日CV 视觉论文速览】29 Nov 2018
    【词云】wordcloud安装与使用
  • 原文地址:https://www.cnblogs.com/boywg/p/4149965.html
Copyright © 2020-2023  润新知