第8章 委托、Lamdba表达式和事件

本章内容:

  •  委托
  •    Lambda表达式
  •  事件

8.1.3   简单的委托示例

 

首先定义一个类MathOperations,它有两个静态方法,对double类型的值执行两个操作。  

public class MathOperations

{

   public static double MultiplyByTwo(double value)

   {

       return value*2;

   }



   public static double Square(double value)

   {

       return value*value;

   }

}

下面使用自定义的委托调用这两个方法

internal delegate double DoubleOp(double x);

 

private static void Main()

{

    DoubleOp[] operations =

    {

         MathOperations.MultiplyByTwo,

         MathOperations.Square

   };

   for (var i = 0; i < operations.Length; i++)

    {

      Console.WriteLine("Using operations[{0}]", i);

      ProcessAndDisplayNumber(operations[i], 2.0);

      ProcessAndDisplayNumber(operations[i], 7.94);

      ProcessAndDisplayNumber(operations[i], 1.141);

      Console.WriteLine();

    }

  Console.ReadLine();
}
private static void ProcessAndDisplayNumber(DoubleOp action, double value) { var result = action(value); Console.WriteLine("Value is {0},result of operation is {1}", value, result); }

运行这个示例,得到如下所示的代码:

 第8章 委托、Lamdba表达式和事件

 

8.1.4  Action<T>和Func<T>委托

 

  除了自定义的委托类型外,我们还可以使用Action<T>委托和Func<T>委托。

  泛型Action<T>委托表示引用一个void返回类型的方法,因为这个委托类存在不同的变体,所以可以传递至多16种不同的参数类型,

没有泛型参数的Action类可调用没有参数的方法。

  Func<T>允许调用带返回类型的方法,与Action<T>类似,Func<T>也定义了不同的变体,至多也可以传递16个参数类型和一个返回类型。

  Func<out TResult>委托类型可以调用带返回类型且无参数的方法。

  

  理解了这两个委托后,我们就可以将上一节种定义的DoubleOp委托删除,改为Func<T>委托,如下所示:  

Func<double,double> [] operations =

{

    MathOperations.MultiplyByTwo,

    MathOperations.Square

};

  然后修改ProcessAndDisplayNumber方法的第一个参数类型,如下所示:

private static void ProcessAndDisplayNumber(Func<double,double> action, double value)

{

    var result = action(value);

    Console.WriteLine("Value is {0},result of operation is {1}", value, result);

}

 

8.1.5  BubbleSorter示例

  现在有一个类BubbleSorter,它有一个静态方法Sort(),这个方法只有一个int数组类型的参数,用来把数组按照升序重新排列。

假如传递给它的是int数组:{0,5,6,2,1},则方法执行完毕后int数组为:{0,1,2,5,6}。

  这就是经典的冒泡排序,是一种简单的排序方法。它适合于一小组数字。下面是代码:

public class BubbleSorter

{

    public static void Sort(int[] array)

    {

        bool swapped;

        do

        {

            swapped = false;

            for (var i = 0; i < array.Length - 1; i++)

            {

               if (array[i] <= array[i + 1])

               {

                   continue;

               }

               var temp = array[i];

               array[i] = array[i + 1];

               array[i + 1] = temp;

               swapped = true;

            }

          } while (swapped);

    }

}

  它非常适合于int,但是我们希望Sort()方法能给任何对象排序,这时上面的比较方法就有问题了,为了实现这个功能,我们要使用泛型方法和

委托。如下所示:

  

public class BubbleSorter

{

    public static void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)

    {

       bool swapped;

       do

       {

           swapped = false;

           for (var i = 0; i < sortArray.Count - 1; i++)

           {

               if (!comparison(sortArray[i + 1], sortArray[i]))

               {

                   continue;

               }

               T temp = sortArray[i];

               sortArray[i] = sortArray[i + 1];

               sortArray[i + 1] = temp;

               swapped = true;

           }

       } while (swapped);

   }

}

为了使用这个类,我们需要定义一个员工类,根据他们的薪水进行排序。

public class Employee

{

    public string Name { get; private set; }



    public decimal Salary { get; private set; }



    public Employee(string name, decimal salary)

    {

        Name = name;

        Salary = salary;

    }



    public override string ToString()

    {

        return string.Format("{0},{1:C}", Name, Salary);

    }



    public static bool CompareSalary(Employee e1, Employee e2)

    {

       return e1.Salary < e2.Salary;

    }
}

下面编写客户端代码,完成排序功能:

private static void Main()

{

    Employee[] employees =

    {

         new Employee("Jerry", 20000),

         new Employee("John", 10000),

         new Employee("Tom", 25000),

         new Employee("Lucy", 1000000.38M),

         new Employee("Lili", 23000)

    };

    BubbleSorter.Sort(employees, Employee.CompareSalary);



    foreach (var employee in employees)

    {

        Console.WriteLine(employee);

    }



    Console.ReadLine();

}

运行这段代码,正确显示按照薪水排列的Employee,如下图所示:

第8章 委托、Lamdba表达式和事件

 

8.1.6  多播委托

  前面使用的每个委托都只包含一个方法调用,委托也可以包含多个方法,这种委托称为多播委托。如果调用多播委托,

就可以按顺序连续调用多个方法。 多播委托可以识别运算符"+","+=","-"和"-="。

  为了说明多播委托的用法,我们修改之前示例中的部分代码,如下所示:

public class MathOperations

{

    public static void MultiplyByTwo(double value)

    {

        var result = value*2;

        Console.WriteLine("{0} Multiply by 2 equlas {1}", value, result);

    }



    public static void Square(double value)

    {

        var result = value*value;

        Console.WriteLine("{0} squaring equlas {1}", value, result);

    }

}

 

 

private static void Main()

{

    Action<double> operations = MathOperations.MultiplyByTwo;

    operations += MathOperations.Square;



    ProcessAndDispalyNumber(operations, 2.0);

    ProcessAndDispalyNumber(operations, 7.94);

    ProcessAndDispalyNumber(operations, 1.414);



    Console.ReadLine();

}



public static void ProcessAndDispalyNumber(Action<double> action, double value)

{

     Console.WriteLine();

     Console.WriteLine("ProcessAndDispalyNumber called with value ={0}", value);

     action(value);

}

 运行代码,得到如下图所示的代码:

第8章 委托、Lamdba表达式和事件

 
使用多播委托应注意,如果通过委托调用的其中一个方法抛出一个异常,整个迭代就会停止,如下面的示例所示:

private static void Main()

{

    Action d1 = One;

    d1 += Two;



    try

    {

        d1();

    }

    catch (Exception)

    {

        Console.WriteLine("Exception caught");

    }



    // 运行结果

    // One

    // Exception caught



    Console.ReadLine();

}



private static void One()

{

    Console.WriteLine("One");

    throw new Exception("Error in one");

}



private static void Two()

{

    Console.WriteLine("Two");

}

 

 从运行结果来看,我们看出委托只调用了第一个方法,因为第一个方法抛出了一个异常,所以委托的迭代会停止,不再调用Two()方法。

 在这种情况下,为了避免这个问题,应自己迭代方法列表,为此我们修改代码如下:

private static void Main()

{

    Action d1 = One;

    d1 += Two;



    Delegate[] delegates = d1.GetInvocationList();

    foreach (Action d in delegates)

    {

        try

        {

            d();

        }

        catch (Exception)

        {

            Console.WriteLine("Exception caught");

        }

    }



    // 运行结果

    // One

    // Exception caught

    // Two



    Console.ReadLine();

}

 

 从运行结果我们可以看到,在捕获了异常后,将继续迭代下一个方法。

 

8.1.7   匿名方法

 

到目前为止,要想使委托工作,方法必须已经存在(即委托是用它将调用的方法的相同签名定义的)。

但还有另外一种使用委托的方式:即通过匿名方法。

下面通过一个简单的例子说明如何使用匿名方法:

private static void Main()

{

    var str = ",Middle part,";

    Func<string, string> anonDel = delegate(string param)

      {

          param += str;

          param += "End part";

          return param;

      };

    Console.WriteLine(anonDel("Strat Part"));



    // 输出结果

    // Strat Part,Middle part,End part



    Console.ReadLine();

}

 

 匿名方法的优点:减少了要编写的代码,不必定义仅由委托使用的方法,有助于降低代码的复杂性。

从C#3.0开始,可以使用Lambda表达式替代匿名方法。

 

 

8.2   Lambda 表达式

自C#3.0开始,可以使用Lambda表达式把实现代码赋予委托,只要有委托参数类型的地方,就可以使用Lambda表达式。

下面将前面使用匿名方法的例子改为使用Lambda表达式:

private static void Main()

{

    var str = ",Middle part,";

    Func<string, string> lambda = param =>

    {

        param += str;

        param += "End part";

        return param;

    };

    Console.WriteLine(lambda("Strat Part"));



    // 输出结果

    // Strat Part,Middle part,End part



    Console.ReadLine();

}

 

从上面的代码可以看出,Lambda运算符"=>"的左边列出了需要的参数,右边定义了赋予lambda变量的方法的实现代码。  

 

8.2.1   参数

Lambda表达式有几种定义参数的方式,如果只有一个参数,只写出参数名就够了,如下所示:

Func<string, string> oneParam = s => string.Format("change uppercase {0}",s.ToUpper());

Console.WriteLine(oneParam("test"));



// 输出结果

// change uppercase TEST

 

 如果委托使用多个参数,就把参数放在花括号中。如下所示:

Func<double, double, double> twoParams = (x, y) => x*y;

Console.WriteLine(twoParams(3, 2));



// 输出结果

// 6

 

也可以在花括号中给变量名添加参数类型:

Func<double, double, double> twoParams = (double  x, double y) => x*y;

 

 

8.2.2  多行代码

如果Lambda表达式只有一条语句,在方法块内就不需要花括号和return语句,因为编译器会添加一条隐式的return语句。

Func<double, double> square = x => x*x;

 

添加花括号、return语句和分号是完全合法的,通常这比不添加这些符号更容易阅读:

Func<double, double> square = x =>

          {

              return x*x;

          };

 

 如果在Lambda表达式的实现代码中需要多条语句,就必须添加花括号和return语句:

var str = ",Middle part,";

Func<string, string> lambda = param =>

{

    param += str;

    param += "End part";

    return param;

};

你可能感兴趣的:(dba)