zoukankan      html  css  js  c++  java
  • C# 什么是委托

    http://www.cnblogs.com/success/articles/1785915.html

    大家在C++学习过程中,一定会接触到各种各样的指针,其中,有一种指针是指向函数或者说是指向方法的,我们可以通过调用这个指针来调用其指向的方法。但是,这样的指针是不安全的。如果我们简单的把C++的指针认为是一个记录内存地址的空间,那么,方法指针里记录的,就是目标方法的调用地址。但是,C++并没有对指针指向的对象加以任何的限制,你不知道这个方法会返回什么,不知道这个方法要接收多少个参数,也不知道接收的参数又是什么类型,并且,在C++中,指针还可以参与运算的,因此,对于调用者而言,除了看到一个地址,其余一概不知道。一切要等到调用以后,才真向大白。这样的指针甚至常常被一些不轨之人所利用,让方法指针指向一个破坏系统的方法,就像给你一把钥匙,让你开一个盒子。盒子里面是什么?也许是蛋糕,也许,那就是潘多拉之盒……谁知道呢。

    由于方法指针不完善,C#针对这一现象进行了改进——委托。委托的作用与方法指针一样,用它来指向一个方法,并且提供给客户程序调用。但是,这个方法指针是有限制的,它必规定好了所指向方法的返回值、参数个数以及各个参数的类型。因此,这把“钥匙”能开蛋糕盒却打不开潘多拉之盒……嗯,感觉安全多了。

    因此,所谓委托,是指确定了目标方法签名的方法指针——如果你认为C#中指针的概念已经被消灭,那么,好吧,换个说法也行:所谓委托,是指可以调用目标方法并且确定了目标方法签名的一种特殊的对象.

    具体到C#中语法中,委托又可以分为委托声明和委托实例两种,而它们又同时都可以简称为委托。所以,当我们说到委托时,需要根据上下文来判断一个委托是指委托声明还是指委托实例。

    委托声明,顾名思意,用来声明委托(实例)所指向的方法的签名。它以delegate关键字开头,后面跟的便是方法签名,例如,当我想要指向的方法为:

    public string MergeString(string s1, string s2)

    我们就可以声明一个有两个string参数并且返回一个string的委托:

    delegate string TestDelegate(string s1, string s2);

    委托声明本身并未指向任何方法,因此,它不可以直接被调用。但是,我们可以通过委托(声明)来实例化对象,这样的对象就被称为委托实例。委托实例的创建与用类实例化一个对象类似,但有一个约定,要把目标方法的方法名作为参数传入,例如:

    public void Print(string s1, string s2)
    {
    TestDelegate testDelegate = new TestDelegate(MergeString);
    string newString=testDelegate(s1, s2)
    Console.WriteLine(newString);
    }

    方法Print中第一行,我们就利用一个委托声明得到了一个委托实例。第二行,调用这个委托。第三行,输出结果。就这么简单。

    好,小结一下,委托的实质是安全的方法指针;委托分为委托声明和委托实例;使用委托时,先声明,然后实例化,最后调用。

    那么,现在就有一个很有趣的问题出现了:我们为什么要用委托?在这个例子中,我不仅仍然要写整个方法,而且又要声明委托、又要实例化……为什么要弄得这么复杂?委托写我们带来了什么?

    委托给我们带来的最大好处时:其可以通过编程方法来动态的调用别的方法。这意味着什么?这意味着,当我们把委托作为参数时,写一个代码模板,就可以让其以一定的方式执行不同的代码。举例说明:

    我们有一个现有的方法,可以打印出当前的时间:

    public void PrintTime()
    {
    Console.WriteLine(DateTime.Now.ToString("HH:MM:ss"));
    }

    我们有另外一个方法,可以打印出当前的日期:

    public void PrintDate()
    {
    Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd"));
    }

    恰巧,或者非常不巧,当前的程序里,这两个方法都需要经常被循环的调用,那么,我们就可以利用一个方法,把这种循环封装起来,以避免不停的在程序里写循环:

    public static void MultipleRun(RunOnce method, int count)
    {
    for (int i = 0; i < count; i++)
    {
    method();
    }
    }

    接下来的事情,就是尽情的调用了,各1000次怎么样?

    TestClass tc = new TestClass();
    MultipleRun(tc.PrintDate, 1000);
    MultipleRun(tc.PrintTime, 1000);

    觉得不过瘾就自己改参数吧。

    最后,贴个完整的、简单到毫无意义的示例源码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace ConsoleApplication4
    {
    class Program
    {
    delegate void RunOnce();
    private static void MultipleRun(RunOnce method, int count)
    {
    for (int i = 0; i < count; i++)
    {
    method();
    }
    }
    
    static void Main(string[] args)
    {
    TestClass tc = new TestClass();
    MultipleRun(tc.PrintDate, 1000);
    MultipleRun(tc.PrintTime, 1000);
    }
    }
    
    class TestClass
    {
    public void PrintTime()
    {
    Console.WriteLine(DateTime.Now.ToString("HH:mm:ss"));
    }
    
    public void PrintDate()
    {
    Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd"));
    }
    }
    }
  • 相关阅读:
    梯度下降进阶
    梯度下降基础
    python---matplotlib
    python---numpy
    浅析Jupyter Notebook
    anaconda安装
    机器学习---导学
    python---线程与进程
    mapping values are not allowed in this context at line 115 column 10
    laravel进行单元测试的时候如何模拟数据库以及mockery的调用
  • 原文地址:https://www.cnblogs.com/luciakally/p/4849269.html
Copyright © 2011-2022 走看看