zoukankan      html  css  js  c++  java
  • C#拾遗系列(1):委托

    一、委托概述

    委托具有以下特点:

    • 委托类似于 C++ 函数指针,但它们是类型安全的。

    • 委托允许将方法作为参数进行传递。

    • 委托可用于定义回调方法。

    • 委托可以链接在一起;例如,可以对一个事件调用多个方法。

    • 方法不必与委托签名完全匹配。(委托中的协变和逆变)

    • C# 2.0 版引入了匿名方法的概念,此类方法允许将代码块作为参数传递,以代替单独定义的方法。C# 3.0 引入了 Lambda 表达式,利用它们可以更简练地编写内联代码块。匿名方法和 Lambda 表达式(在某些上下文中)都可编译为委托类型。这些功能统称为匿名函数

    二、委托申明和使用示例

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

     

    //Description: 委托三种声明方式.net1.0到.net3.0

     

    namespace NetTest

        public class TestDelegateNet1To3

        {

            #region 不带返回值的委托

            delegate void TestDelegate(string s);

            public void TestAllDelegate()

            {

                //.net 1.0

                TestDelegate en = new TestDelegate(M);

                en("jack");

                TestDelegate china = China;

                china("wangdeshui");

     

                //.net 2.0

                TestDelegate delegateNet2 = delegate(string s) { Console.Out.WriteLine(s + ":这是一个2.0的方式"); };

                delegateNet2("2.0 delegate declare");

     

                //.net 3.0

                TestDelegate delegateNet3 = (string s) => { Console.Out.WriteLine(s + ":这是3.0的方式"); };

                delegateNet3("3.0 delegate declare");

     

                TestReturn(); 

     

                Func<int, bool> d = (n) => { return n > 10 ? true : false; };

                Console.Out.WriteLine("func<int, bool>:" + d(20));

     

     

            }

     

            void M(string s)

            {

                Console.Out.WriteLine(s + ":Good Morning");

            }

     

            void China(string s)

            {

                Console.Out.WriteLine(s + ":早上好");

            }

            #endregion

     

     

            #region 带返回值的委托测试

     

            delegate bool TestDelegateReturn(int i);

            void TestReturn()

            {

                TestDelegateReturn Net10 = new TestDelegateReturn(IsBig);

                bool i = Net10(11);

                Console.Out.WriteLine("Net10:" + i);

     

                TestDelegateReturn Net20 = delegate(int j)

                {

                    return (j > 10 ? true : false);

     

                };

     

                bool test20 = Net20(5);

                Console.Out.WriteLine("Net20:" + test20);

     

                TestDelegateReturn Net30 = (int k) => { return (k > 10 ? true : false); };

                bool test30 = Net30(50);

                Console.Out.WriteLine("Net30:" + test30);

     

            }

     

            bool IsBig(int i)

            {

                return i > 10 ? true : false;

     

            }

     

            #endregion

        }

    }

     

    三、多播委托

    调用委托时,它可以调用多个方法。这称为多路广播。若要向委托的方法列表(调用列表)中添加额外的方法,只需使用加法运算符或加法赋值运算符(“+”或“+=”)添加两个委托。

    注意:

    作为委托参数传递的方法必须与委托声明具有相同的签名。
    委托实例可以封装静态或实例方法。
    尽管委托可以使用 out 参数,但建议您不要将其用于多路广播事件委托,因为您无法知道哪个委托将被调用。

    多播委托传递值类型时,对参数的修改不会传到下一个
    多播委托传递引用类型时,对引用参数的修改会传到下一个

    示例:

    public class TestMultiCastDelegate

        {

            public delegate void Del(string message);

            delegate void DelRef(Person p);

            void MethodA(string s)

            {

                Console.Out.WriteLine("Method A:"+s);

            }

     

            void MethodB(string s)

            {

                s = "good";

                Console.Out.WriteLine("Method B:" + s);

            }

     

            void MethodC(string s)

            {

                Console.Out.WriteLine("Method C:" + s);

            }

     

            /*

            多播委托传递值类型时,对参数的修改不会传到下一个

            多播委托传递引用类型时,对引用参数的修改会传到下一个

            *

            * */

     

            public void Test()

            {

                //多播委托

                Del d1 = MethodA;

                Del d2 = MethodB;

                Del d3=MethodC;

                Del AllDelegateMethod = d1 + d2;

                AllDelegateMethod += d3;

                AllDelegateMethod("Jack");

                /*

                output:

                ConsoleA: Jack

                ConsoleB: Jack

                ConsoleC:Jack           

                */

                Console.Out.WriteLine("---------------------");

                AllDelegateMethod -= d3;

                AllDelegateMethod("wangds");

     

     

                //测试引用类型

                DelRef dref1 = MethodRefA;

                DelRef dref2 = MethodRefB;

                DelRef Alldref = dref1 + dref2;

                Person p = new Person { Address = "USA", Name = "America" };

                Alldref(p);

            }

     

     

            //内嵌类

     

            class Person

            {

                public string Name { get; set; }

                public string Address { getset; }

     

            }

     

            void MethodRefA(Person p)

            {

                Console.Out.WriteLine(p.Name + "," + p.Address);

                Console.Out.WriteLine("----------MethodRefA---------");

                p.Name = "Jack";

                p.Address = "China";

     

                Console.Out.WriteLine(p.Name + "," + p.Address);

                Console.Out.WriteLine("----------MethodRefA---------");

            }

     

            void MethodRefB(Person p)

            {

                Console.Out.WriteLine("--------MethodRefB----------");

                Console.Out.WriteLine(p.Name + "," + p.Address);

                Console.Out.WriteLine("--------MethodRefB----------");

            }

        }

    四、委托中的协变和逆变

    委托中的协变: 就是允许方法的返回类型是委托的返回类型的子类

    委托中的逆变,方法的参数是委托的参数的基类

    示例:

    #region 委托中的协变: 就是允许方法的返回类型是委托的返回类型的子类

        class Mammals

        {

        }

     

        class Dogs : Mammals

        {

        }

     

        class XieBianDelegate

        {

            // Define the delegate.

            public delegate Mammals HandlerMethod();

     

            public Mammals FirstHandler()

            {

                return null;

            }

     

            public Dogs SecondHandler()

            {

                return null;

            }

     

            void Test()

            {

                HandlerMethod handler1 = FirstHandler;

     

                // Covariance allows this delegate.

                HandlerMethod handler2 = SecondHandler;

            }

        }

        #endregion

     

     

        #region   委托中的逆变,方法的参数是委托的参数的基类

        class NiBianDelegate

        {

            public delegate Dogs HandlerNibianMethod(Dogs dogs);

            public Dogs FirstHandlerNibian(Mammals mammals)

            {

                return null;

            }

     

            void Test()

            {

                Dogs dogs = new Dogs();

                HandlerNibianMethod handNibian1 = FirstHandlerNibian;

     

            }

     

        }

        #endregion

    五、委托综合示例(来自MSDN)

    下面的示例阐释声明、实例化和使用委托。BookDB 类封装一个书店数据库,它维护一个书籍数据库。它公开 ProcessPaperbackBooks 方法,该方法在数据库中查找所有平装书,并对每本平装书调用一个委托。使用的 delegate 类型名为 ProcessBookDelegate。Test 类使用该类打印平装书的书名和平均价格。

    委托的使用促进了书店数据库和客户代码之间功能的良好分隔。客户代码不知道书籍的存储方式和书店代码查找平装书的方式。书店代码也不知道找到平装书后将对平装书执行什么处理。

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

     

    namespace Bookstore

    {

        using System.Collections;

     

        // Describes a book in the book list:

        public struct Book

        {

            public string Title;        // Title of the book.

            public string Author;       // Author of the book.

            public decimal Price;       // Price of the book.

            public bool Paperback;      // Is it paperback?

     

            public Book(string title, string author, decimal price, bool paperBack)

            {

                Title = title;

                Author = author;

                Price = price;

                Paperback = paperBack;

            }

        }

     

        // Declare a delegate type for processing a book:

        public delegate void ProcessBookDelegate(Book book);

     

        // Maintains a book database.

        public class BookDB

        {

            // List of all books in the database:

            ArrayList list = new ArrayList();

     

            // Add a book to the database:

            public void AddBook(string title, string author, decimal price, bool paperBack)

            {

                list.Add(new Book(title, author, price, paperBack));

            }

     

            // Call a passed-in delegate on each paperback book to process it:

            public void ProcessPaperbackBooks(ProcessBookDelegate processBook)

            {

                foreach (Book b in list)

                {

                    if (b.Paperback)

                        // Calling the delegate:

                        processBook(b);

                }

            }

        }

    }

     

     

     

     

    // Using the Bookstore classes:

    namespace BookTestClient

    {

        using Bookstore;

     

        // Class to total and average prices of books:

        class PriceTotaller

        {

            int countBooks = 0;

            decimal priceBooks = 0.0m;

     

            internal void AddBookToTotal(Book book)

            {

                countBooks += 1;

                priceBooks += book.Price;

            }

     

            internal decimal AveragePrice()

            {

                return priceBooks / countBooks;

            }

        }

     

        // Class to test the book database:

      public  class TestBookDB

        {

            // Print the title of the book.

            void PrintTitle(Book b)

            {

                System.Console.WriteLine("   {0}", b.Title);

            }

     

            // Execution starts here.

            public void Test()

            {

                BookDB bookDB = new BookDB();

     

                // Initialize the database with some books:

                AddBooks(bookDB);

     

                // Print all the titles of paperbacks:

                System.Console.WriteLine("Paperback Book Titles:");

     

                // Create a new delegate object associated with the static

                // method Test.PrintTitle:

                bookDB.ProcessPaperbackBooks(PrintTitle);

     

                // Get the average price of a paperback by using

                // a PriceTotaller object:

                PriceTotaller totaller = new PriceTotaller();

     

                // Create a new delegate object associated with the nonstatic

                // method AddBookToTotal on the object totaller:

                bookDB.ProcessPaperbackBooks(totaller.AddBookToTotal);

     

                System.Console.WriteLine("Average Paperback Book Price: ${0:#.##}",

                        totaller.AveragePrice());

            }

     

            // Initialize the book database with some test books:

            static void AddBooks(BookDB bookDB)

            {

                bookDB.AddBook("The C Programming Language", "Brian W. Kernighan and Dennis M. Ritchie", 19.95m, true);

                bookDB.AddBook("The Unicode Standard 2.0", "The Unicode Consortium", 39.95m, true);

                bookDB.AddBook("The MS-DOS Encyclopedia", "Ray Duncan", 129.95m, false);

                bookDB.AddBook("Dogbert's Clues for the Clueless", "Scott Adams", 12.00m, true);

            }

        }

    }

     

     

    扫码关注公众号,了解更多管理,见识,育儿等内容

    作者: 王德水
    出处:http://www.cnblogs.com/cnblogsfans
    版权:本文版权归作者所有,转载需经作者同意。

  • 相关阅读:
    windows下cmd命令行上传代码到github的指定库
    Navicat Premium 12.1.11.0安装与激活
    windows部署Apollo
    C#事件-使用事件需要的步骤
    C#委托和事件
    C#事件委托概念
    C#中委托和事件的区别
    C#委托与事件
    C#细说多线程
    C# 堆栈(Stack)和队列(Queue)
  • 原文地址:https://www.cnblogs.com/cnblogsfans/p/1217381.html
Copyright © 2011-2022 走看看