C#委托学习笔记

1、C#委托是什么

c#中的委托可以理解一种类,这种类实例化后可以将函数的包装成一个变量(该变量就变成了对该函数的“引用”),它使得这个变量(函数)可以作为参数来被传递,这在作用上相当于c中的函数指针。c用函数指针获取函数的入口地址,然后通过这个指针来实现对函数的操作。

委托的定义和方法的定义类似,只是在定义的前面多了一个delegate关键字。如下定义:

public delegate void MyDelegate(int para1, string para2);

委托能包装的方法是有一定限制的,例如能被前面的委托类型MyDelegate包装的方法需要满足以下条件:

1.方法的返回类型必须为void;

2.方法必须有两个参数,并且第一个参数应为int类型,第二个参数为string类型。

总结:可以被委托包装的方法必须满足以下规则:

1.方法的签名必须与委托一致,方法签名包括参数的个数、类型和顺序;

2.方法的返回类型要和委托一致,注意,方法的返回类型不属于方法签名的一部分。

2、委托的使用

//委托使用的演示
class Program
{        
    public delegate void MyDelegate(int para1, int para2);  //1.使用delegate关键字来定义一个委托类型
    static void Main(string[] args)
    {            
        MyDelegate d;   //2.声明委托变量d
        
        d = new MyDelegate(new Program().Add);  //3.实例化委托类型,传递的方法也可以为静态方法,这里传递的是实例方法
        
        MyMethod(d);    //4.委托类型作为参数传递给另一个方法

        Console.ReadKey();
    }

    void Add(int para1, int para2)
    {
        int sum = para1 + para2;
        Console.WriteLine("两个数的和为:" + sum);
    }

    private static void MyMethod(MyDelegate mydelegate)
    {            
        mydelegate(1, 2);   //5.在方法中调用委托
    }
}

从以上代码可以看出,使用委托的步骤为:定义委托类型—声明委托变量实例化委托将实例作为参数传递给另一个方法该方法调用委托。如下具体分析委托的使用过程。

(1)定义委托类型: public delegate void MyDelegate(int a, int b);。其定义方式类似于方法的定义,只是多了一个delegate关键字。

(2)声明委托变量:MyDelegate d;既然委托是一种类型,那么可以使用委托来声明一个委托变量,相当于int a;

(3)实例化委托: d = new MyDelegate(new Program().Add);。第二步只是声明了委托变量,但并没有将它实例化。类的实例化使用new关键字来实现,而委托也属于一种“类”,所以委托的实例化也使用new关键字来进行的。这里需要注意的是,委托的实例化是用一个方法名(不能带左右括号)作为参数,并且该方法的定义必须符合委托的定义,即该方法的返回类型、参数个数和类型必须与委托定义中的一样。这样,前面3步就好比构造了一个律师对象,而方法InstanceMethod好比是当事人的方法。

(4)作为参数传递给方法:MyMethod(d);。委托使用得在C#中,可以把一个方法作为另一个方法的参数,而委托可以看作是一个包装方法的对象。

(5)在方法中调用委托。MyMethod(d);。委托使用得在c#中,可以把一个方法作为另一个方法的参数,而委托可以看作是一个包装方法的对象。

总结:在使用委托时,需要注意以下几个问题。

1.在第三步中,被传递的方法的定义必须与委托定义相同,即方法的返回类型和参数个数、参数类型都必须与委托相同。并且,传递的是方法名,方法名后不能带有左右括号。

2.在第五步中,委托的调用与方法调用类似,传递的实参类型和个数必须与委托定义一致。

3.由于委托是方法的包装类型,所以对委托的调用也就是对其所包装的的方法的调用,上面第5步时间上是调用了Add方法来对传入的实参进行计算。

3、委托链的使用

委托链其实就是委托类型,只是委托链把多个委托链接在一起而已,也就是说,我们把链接了多个方法的委托称为委托链或多路广播委托。如下:

public delegate void DelegateTest();
static void Main(string[] args)
{
    //用静态方法来实例化委托
    DelegateTest dtstatic = new DelegateTest(Program.method1);      
    DelegateTest dtinstance = new DelegateTest(new Program().method2);
    
    DelegateTest delegatechain = null;  //定义一个委托对象,一开始初始化为null,即不代表任何方法。

    //使用 “+”符号链接委托,链接多个委托后就成为了委托链
    delegatechain += dtstatic;
    delegatechain += dtinstance;

    //调用委托链
    delegatechain();    // 输出两行"这是静态方法"和"这是实例方法"
    Console.Read();
}
private static void method1()
{
    Console.WriteLine("这是静态方法");
}

private void method2()
{
    Console.WriteLine("这是实例方法");
}

从委托链中移除委托

//使用 “+”符号链接委托,链接多个委托后就成为了委托链
delegatechain += dtstatic;
delegatechain += dtinstance;

//使用 “-”运算符 移除委托 
delegatechain -= dtstatic;

4、什么要使用委托

上一章中我们可能会很疑惑,为什么需要委托?为什么不直接在MyMethod方法里直接调用Add方法,反而要实例化一个委托对象来完成调用呢?这岂不是自找麻烦吗?

当然,c#引入委托并不是自找麻烦。委托是c#最好的一个特性,它为后来的很多特性都打下了基础。委托使得一个方法可以作为另一个方法的参数进行传递,这就是委托最大的作用。如下例子:

例如我们要实现一个打招呼的方法,而每个国家打招呼的方式都不一样,刚开始我们可能会像下面这样实现打招呼的方法:

public void Greeting(string name, string language)
{
    switch (language)
    {
        case "zh-cn":
            ChineseGreeting(name);
            break;
        case "en-us":
            EnglishGreeting(name);
            break;
        default:
            EnglishGreeting(name);
            break;
    }
}

public void EnglishGreeting(string name)
{
    Console.WriteLine("Hello, " + name);
}

public void ChineseGreeting(string name)
{
    Console.WriteLine("你好, " + name);
}

若后续我们需要添加德国、日本等打招呼方法,就必须修改Greeting方法内的case语句,来适应新的需求,这样特别不方便。有了委托,我们就可以把函数作为参数,并像如下代码实现Greeting方法:

public delegate void GreetingDelegate(string name);
static void Main(string[] args)
{
    //引入委托
    Program p = new Program();
    p.Greeting("小叶", p.ChineseGreeting);        // 将所使用的的方法ChineseGreeting作为参数传递
    p.Greeting("Tommy Li", p.EnglishGreeting);
    Console.Read();
}

public void Greeting(string name, GreetingDelegate callback)
{
    callback(name); // 调用ChineseGreeting方法
}
public void EnglishGreeting(string name)
{
    Console.WriteLine("Hello, " + name);
}
public void ChineseGreeting(string name)
{
    Console.WriteLine("你好, " + name);
}

文章转载自:【c# 学习笔记】c#委托是什么

5、例子

class Program
{
    static void Main(string[] args)
    {
        Bookstore XinHua = new Bookstore(); // 实例化一个新化书店
        Reader LiNing = new Reader();       // 实例化一个读者李宁

        // 以new关键字实例化一个委托绑定到onpublish事件
        // 传递的方法可以是“静态方法”,也可以是“实例方法”
        XinHua.onpublish += new Bookstore.publish(LiNing.issue);
        XinHua.issue();
        Console.ReadKey();
    }
}

public class Bookstore
{
    public delegate void publish(); //声明委托
    public event publish onpublish; //声明委托注册的事件

    //声明触发方法
    public void issue()
    {
        Console.WriteLine("书店发布了一本杂志");
        this.onpublish();
    }
}

public class Reader
{
    // 定义收到杂志的方法
    public void issue()
    {
        Console.WriteLine("我收到了一本杂志");
    }
}

运行结果:

示例2

class Program
{
    static void Main(string[] args)
    {
        Bookstore XinHua = new Bookstore();  // 实例化一个新化书店
        Reader LiNing = new Reader("李宁");  // 实例化一个读者李宁,订阅了电脑类杂志
        Reader LiSi = new Reader("李四");    // 实例化一个读者李四,订阅了英语类杂志

        // 李宁只订阅电脑类杂志事件
        XinHua.on_publish_computer += new Bookstore.publish_computer(LiNing.received_book);

        // 李四两种杂志都订阅了
        XinHua.on_publish_computer += new Bookstore.publish_computer(LiSi.received_book);
        XinHua.on_publish_english += new Bookstore.publish_english(LiSi.received_book);

        XinHua.issue_computer();
        XinHua.issue_english();

        Console.ReadKey();
    }
}

public class Bookstore
{
    // 委托有些类“似于”类的静态方法, 可以通过Bookstore.publish_computer进行访问
    public delegate void publish_computer(string bookname); //声明电脑的委托
    public event publish_computer on_publish_computer;      //声明委托注册的事件

    //发行电脑的触发方法
    public void issue_computer()
    {
        Console.WriteLine("书店发布了一本电脑类杂志");
        this.on_publish_computer("《电脑周报》");
    }

    public delegate void publish_english(string bookname); //声明自己的委托        
    public event publish_english on_publish_english;      //声明委托注册的事件

    //发行电脑的触发方法
    public void issue_english()
    {
        Console.WriteLine("书店发布了一本英语类杂志");
        this.on_publish_english("《英语周报》");
    }
}

public class Reader
{
    public string name;
    public Reader(string n) { this.name = n; }
    // 定义收到杂志的方法
    public void received_book(string bookname)
    {
        Console.WriteLine(this.name + "收到了一本杂志" + bookname);
    }
}

运行结果:

示例3

class Program
{
    static void Main(string[] args)
    {
        Bookstore XinHua = new Bookstore("新华书店"); // 实例化一个新化书店
        Reader LiNing = new Reader("李宁");           // 实例化一个读者李宁,订阅了电脑类杂志
        Reader LiSi = new Reader("李四");             // 实例化一个读者李四,订阅了英语类杂志

        // 李宁和李四订阅电脑类杂志事件
        XinHua.PubComputer += new Bookstore.PubComputerEventHandler(LiNing.received_book);
        XinHua.PubComputer += new Bookstore.PubComputerEventHandler(LiSi.received_book);

        // 李四订阅了英语类杂志事件
        XinHua.PubEnglish += new Bookstore.PubEnglishEventHandler(LiSi.received_book);

        // 手动触发执行发布杂志事件
        XinHua.issueComputer("电脑周刊", Convert.ToDateTime("2019.10.29"));
        XinHua.issueEnglish("英语周刊", Convert.ToDateTime("2018.09.29"));
        Console.ReadKey();
    }
}
    
// 发布事件类
public class PubEventArgs : EventArgs
{
    public string bookName;
    public DateTime bookTime;
    //构造函数
    public PubEventArgs(string name, DateTime time) { this.bookName = name; this.bookTime = time; }
}

public class Bookstore
{
    public string name;
    public Bookstore(string n) { this.name = n; }

    // 委托有些类似于类的静态方法, 可以通过Bookstore.PubComputerEventHandler进行访问
    // 委托实际上是一种类,可以使用new关键字实例化
    // 实例化后,就是对函数包装(引用),使得函数可以作为参数传递或赋值。
    public delegate void PubComputerEventHandler(object sender, PubEventArgs e); //声明电脑的委托
    public event PubComputerEventHandler PubComputer;                            //声明委托注册的事件

    //发行电脑的触发方法
    public void issueComputer(string bookname, DateTime booktime)
    {
        Console.WriteLine("书店发布了一本电脑类杂志");
        this.PubComputer(this, new PubEventArgs(bookname, booktime));
    }

    public delegate void PubEnglishEventHandler(object sender, PubEventArgs e); //声明自己的委托        
    public event PubEnglishEventHandler PubEnglish;                             //声明委托注册的事件

    //发行电脑的触发方法
    public void issueEnglish(string bookname, DateTime booktime)
    {
        Console.WriteLine("书店发布了一本英语类杂志");
        this.PubEnglish(this, new PubEventArgs(bookname, booktime));
    }
}

public class Reader
{
    public string name;
    public Reader(string n) { this.name = n; }

    // 定义收到杂志的方法,参数不是一个简单的变量而是PubEventArgs实例
    public void received_book(object sender, PubEventArgs e)
    {
        Bookstore bs = (Bookstore)sender;
        Console.WriteLine(string.Format("{0}收到了{1}书店发布的《{2}》 发布时间:{3}", this.name, bs.name, e.bookName, e.bookTime));
    }
}

允行结果:

posted @ 2019-10-30 09:33  Rain Man  阅读(1639)  评论(1编辑  收藏  举报