zoukankan      html  css  js  c++  java
  • 从函数到委托

    第二章 从函数到委托

    看这样的程序:

    C# code
     
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int sum = 0;
    for (int i = 1; i <= 100; i++)
    {
        sum = sum + i;
    }
    Console.WriteLine(sum);
    sum = 0;
    for (int i = 1; i <= 1000; i++)
    {
        sum = sum + i;
    }
    Console.WriteLine(sum);



    这个程序有一点点呆,很明显程序的前半部分和后半部分拥有类似的结构,只是控制i的循环变量的终值不同。因此我们可以提取出一个函数:

    C# code
     
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    static void Main(string[] args)
    {
        Console.WriteLine(Sum(100));
        Console.WriteLine(Sum(1000));
    }
     
    static int Sum(int n)
    {
        int sum = 0;
        for (int i = 1; i <= n; i++)
        {
            sum = sum + i;
        }
        return sum;
    }


    现在我们需要统计1~100中所有偶数的和,怎么做呢?很简单,加上一个判断:

    C# code
     
    ?
    1
    2
    3
    4
    5
    6
    int sum = 0;
    for (int i = 1; i <= 100; i++)
    {
        if (i % 2 == 0) sum = sum + i;
    }
    Console.WriteLine(sum);



    这个程序和那个Sum函数的结构还是类似的,我们能不能写出一个通用的函数呢?有的人不假思索地这么写:

    C# code
     
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    static int Sum(int n, int type)
    {
        int sum = 0;
        for (int i = 1; i <= n; i++)
        {
            if (type == 1)
            { sum = sum + i; }
            if (type == 2)
            if (i % 2 == 0) sum = sum + i; }
        }
        return sum;
    }


    这样type传1就可以累加所有的数字,传2只累加偶数。

    那么现在要累加奇数怎么办呢?再加一个type=3吧。如果要累加质数呢?……你开始抱怨,需求怎么能这么多呢。

    我们再仔细看下代码,我们发现,这些需求虽然都有差异,但是程序中唯一变化的只有循环体中的判断。我们有没有办法将这个判断作为参数传进去呢?如果可以,那么问题似乎就解决了。

    委托就是这样一种类型,它这种类型代表一段代码,使得我们可以将它作为参数传给函数,函数将它放入那个需要变化的地方执行,从而允许我们作为调用者来自定义函数的一些行为。

    C# code
     
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    delegate bool PredicateDelegate(int n); // 定义委托
     
    static bool foo1(int n)
    {
        return true;
    }
     
    static bool foo2(int n) // 将Sum函数中可变的代码抽取出来作为一个函数
    {
        return n % 2 == 0;
    }
     
    static void Main(string[] args)
    {
        Console.WriteLine(Sum(100, foo2)); // 将函数作为参数传给Sum
    }
     
    static int Sum(int n, PredicateDelegate pred)
    {
        int sum = 0;
        for (int i = 1; i <= n; i++)
        {
            if (pred(i)) sum = sum + i; // 将委托的代码放在原先的位置上
        }
        return sum;
    }



    当我们传入foo2,那么Sum只累加偶数,如果传入foo1,那么Sum全部累加。如果只累加奇数呢?我们只要定义一个函数:

    C# code
     
    ?
    1
    bool foo3(int n) { return n % 2 != 0; }



    即可。
    请注意,foo1 foo2 foo3虽然和 Sum 写在一起,但是在这里,我们将它视为函数的调用者定义的,而不是编写Sum的那个人定义的。我们可以无限扩展。编写出foo4 foo5……,从而实现各种不同的需求。

    fooN 这个函数有什么特点呢?它们是为了给Sum提供参数而临时定义的,事实上,我们也不打算在别的地方再利用它,那么这个函数的函数名作为实参其实没有什么用,所以C#提供了一种更简单的写法:

    C# code
     
    ?
    1
    Console.WriteLine(Sum(100, delegate(int n) { return n % 2 == 0; }));



    在这里,我们将foo2这个函数的定义合并到了对Sum的调用中,它看上去更像一个参数。

    还不够简单?也许你觉得已经很简单了,但是的确C#提供了更简单的写法,那就是Lambda表达式:它的写法是

    (参数列表) => { 语句体 }

    当语句体中的语句只有1行,并且是 return 表达式; 这样的形式的时候,我们可以省略花括号,直接写表达式。更cool的是,Lambda可以自动推定参数的类型,于是int n中的int也省下了。

    C# code
     
    ?
    1
    Console.WriteLine(Sum(100, (n) => n % 2 == 0));



    C#还特别规定,当参数只有1个的时候,括号也可以省略,所以上面的代码的最终形式可以表示成:

    C# code
     
    ?
    1
    Console.WriteLine(Sum(100, n => n % 2 == 0));



    思考下,我们对foo1如何改写?

    C# code
     
    ?
    1
    Console.WriteLine(Sum(100, n => true));

     

  • 相关阅读:
    java excel转pdf 工具类
    java word转pdf 工具类
    如何向数据库中添加TIMESTAMP(6)类型的数据
    IE浏览器 div或者其他容器的height属性无效 滚动条问题解决办法
    ComboBox赋值ItemsSource数据源的时候会触发SelectionChanged改变事件的解决办法
    devexpress chart 散点图加载并分组显示(可以自定义颜色 同组中的点颜色相同)
    myEclipse mybatis自动生成工具xml配置
    MySQL日志简介
    存储引擎简介
    索引介绍
  • 原文地址:https://www.cnblogs.com/sukhoi/p/7419115.html
Copyright © 2011-2022 走看看