本章内容:

  •  委托
  •    Lambda表达式
  •  事件

8.1.3   简单的委托示例

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

public class MathOperations
{
   public static double MultiplyByTwo(double value)
   {
       ;
   }

   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
   };
   ; 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.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;
            ; i < array.Length - ; i++)
            {
               ])
               {
                   continue;
               }
               var temp = array[i];
               array[i] = array[i + ];
               array[i + ] = 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;
           ; i < sortArray.Count - ; i++)
           {
               ], sortArray[i]))
               {
                   continue;
               }
               T temp = sortArray[i];
               sortArray[i] = sortArray[i + ];
               sortArray[i + ] = 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("Lucy", 1000000.38M),
         )
    };
    BubbleSorter.Sort(employees, Employee.CompareSalary);

    foreach (var employee in employees)
    {
        Console.WriteLine(employee);
    }

    Console.ReadLine();
}

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

8.1.6  多播委托

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

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

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

public class MathOperations
{
    public static void MultiplyByTwo(double value)
    {
        ;
        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);
}

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

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

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(, ));

// 输出结果

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

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;
};

第8章 委托、Lamdba表达式和事件的更多相关文章

  1. C#高级编程9-第8章 委托、lamdba表达式和事件

    委托.lamdba表达式和事件 1.引用方法 函数指针是一个指向内存位置的指针,不是类型安全的.无法判断实际指向.参数和返回类型也无从知晓..NET委托是类型安全的.定义了返回类型和参数类型,不仅包含 ...

  2. C#编程 委托 Lambda表达式和事件

    委托 如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托 在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方 ...

  3. C#学习笔记三(委托·lambda表达式和事件,字符串和正则表达式,集合,特殊的集合)

    委托和事件的区别 序号 区别 委托 事件 1 是否可以使用=来赋值 是 否 2 是否可以在类外部进行调用 是 否 3 是否是一个类型 是 否,事件修饰的是一个对象 public delegate vo ...

  4. C#高级编程(第9版) 第08章 委托、lambda表达式和事件 笔记

          本章代码分为以下几个主要的示例文件: 1. 简单委托 2. 冒泡排序 3. lambda表达式 4. 事件示例 5. 弱事件     引用方法 委托是寻址方法的.NET版本.在C++中函数 ...

  5. 第八章 委托,lamdbda 表达式和事件

    第八章 委托,lamdbda 表达式和事件 委托是寻址方式的.net版本. 委托是类型安全的类,它定义了返回类型和参数的类型.委托类不仅包含方法的应用,也可以包含对多个方法的引用. 在 C++中,函数 ...

  6. 《C#从现象到本质》读书笔记(六)第8章委托和事件

    <C#从现象到本质>读书笔记(六)第二部分 C#特性 第8章委托和事件 从这一部分开始,知识点就相对少了,重要的是代码练习.奈何太高深的代码平常不怎么用,这些特性也不是经常写代码的. 委托 ...

  7. C# 委托、lambda表达式和事件

    什么是委托?委托就是持有一个或多个方法的对象,并且该对象可以执行,可以传递. using System; using System.Collections.Generic; using System. ...

  8. 委托、Lambda表达式、事件系列07,使用EventHandler委托

    谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...

  9. 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别

    在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...

随机推荐

  1. hdoj-1233-还是畅通工程

    题目:hdoj-1233 题解: 本题是典型的最小生成树问题,给出的是无向图,这里使用的方法是Prim最小生成树算法. Reference Prim算法参照:最小生成树-Prim算法和Kruskal算 ...

  2. Memcached 笔记与总结(3)安装 php-memcache(windows 系统下)

    在 windows 下安装 php-memcache,需要下载编译好的 memcached.dll. 要找到可用的 dll 文件,需要根据 php.ini 中的 3 个参数来选择 dll 文件: ① ...

  3. php字符串常用函数

    addslashes print addslahes ('She said, "Great!"'); #output #She said, \"Great!\ echo ...

  4. MySQL 5.6.3

    SHOW VARIABLES LIKE '%version%'; https://dev.mysql.com/doc/refman/5.6/en/explain.html As of MySQL 5. ...

  5. Linux的权限说明

    Linux的权限不是很细致,只有RWX三种r(Read,读取):对文件而言,具有读取文件内容的权限:对目录来说,具有浏览目录的权限.w(Write,写入):对文件而言,具有新增,修改,删除文件内容的权 ...

  6. openCV中IplImage的使用

    http://blog.csdn.net/welcome_xu/article/details/7650680 IplImage结构详细分析   IplImage 结构解读: typedef stru ...

  7. [收藏]NET技术+25台服务器怎样支撑世界第54大网站

    .NET技术+25台服务器怎样支撑世界第54大网站   英文原文:StackOverflow Update: 560M Pageviews A Month, 25 Servers, And It's ...

  8. Qt 之 自定义提示信息框—迅雷风格(模拟QDialog类的exec()方法) good

    http://blog.csdn.net/goforwardtostep/article/details/53614830

  9. TP自带的缓存机制

    原文章出处: http://blog.163.com/liwei1987821@126/blog/static/172664928201422133218356/ 动态缓存  Cache缓存类 vie ...

  10. JS-006-表格元素操作

    直接上菜咯... 以下为 HTML 表格源码: <html> <head> <meta http-equiv="Content-Type" conte ...