C#高阶-委托和时间及lamda表达式

委托Delegate

    C#中的Delegate对应于C中的指针,但是又有所不同C中的指针既可以指向方法,又可以指向变量,并且可以进行类型转换,

C中的指针实际上就是内存地址变量,他是可以直接操作内存的,通过内存地址直接访问变量,直接调用方法。

    而C#中的Delegate是强类型的,也就是说在声明委托时就已经指定了该变量只能指向具有特定参数,以及返回值的方法。

   使用delegate就可以直接建立任何名称的委托类型,当进行系统编译时,系统就会自动生成此类型。您可以使用delegate void MyDelegate()

方式建立一个委托类,并使用ILDASM.exe观察其成员。由ILDASM.exe 中可以看到,它继承了System.MulticastDelegate类,

并自动生成BeginInvoke、EndInvoke、Invoke 等三个常用方法。

Invoke 方法是用于同步调用委托对象的对应方法,而BeginInvoke、EndInvoke是用于以异步方式调用对应方法的。

public class MyDelegate:MulticastDelegate

     {

         //同步调用委托方法

         public virtual void Invoke();

         //异步调用委托方法

         public virtual IAsyncResult BeginInvoke(AsyncCallback callback,object state);

         public virtual void EndInvoke(IAsyncResult result);

     }

MulticastDelegate是System.Delegate的子类,它是一个特殊类,编译器和其他工具可以从此类派生,但是自定义类不能显式地从此类进行派生。它支持多路广播委托,并拥有一个带有链接的委托列表,在调用多路广播委托时,系统将按照调用列表中的委托出现顺序来同步调用这些委托。

MulticastDelegate具有两个常用属性:Method、Target。其中Method 用于获取委托所表示的方法Target 用于获取当前调用的类实例。

委托的使用

当建立委托对象时,委托的参数类型必须与委托方法相对应。只要向建立委托对象的构造函数中输入方法名称example.Method,委托就会直接绑定此方法。使用myDelegate.Invoke(string message),就能显式调用委托方法。但在实际的操作中,我们无须用到 Invoke 方法,而只要直接使用myDelegate(string message),就能调用委托方法。

无返回值的委托

class Program

    {

        delegate void MyDelegate(string message);

        public class Example

        {

            public void Method(string message)

            {

                MessageBox.Show(message);

            }

        }

        static void Main(string[] args)

        {

            Example example=new Example();

            MyDelegate myDelegate=new MyDelegate(example.Method);

            myDelegate("Hello World");


        }

    }

有返回值的委托

class Program

    {

        delegate string MyDelegate(string message);

        public class Example

        {

            public string Method(string name)

            {

                return "Hello " + name;

            }

        }

        static void Main(string[] args)

        {

            Example example=new Example();

            //绑定委托方法

            MyDelegate myDelegate=new MyDelegate(example.Method);

            //调用委托,获取返回值

            string message = myDelegate("Leslie");

            Console.WriteLine(message);


        }

    }

多路广播委托

delegate double MyDelegate(double message);

        public class Price

        {

            public double Ordinary(double price)

            {

                double price1 = 0.95 * price;

                Console.WriteLine("Ordinary Price : "+price1);

                return price1;

            }

            public double Favourable(double price)

            {

                double price1 = 0.85 * price;

                Console.WriteLine("Favourable Price : " + price1);

                return price1;

            }

            static void Main(string[] args)

            {

                Price price = new Price();

                //绑定Ordinary方法

                MyDelegate myDelegate = new MyDelegate(price.Ordinary);

                //绑定Favourable方法

                myDelegate += new MyDelegate(price.Favourable);

                //调用委托

                Console.WriteLine("Current Price : " + myDelegate(100));


            }

        }

泛型委托

public class Worker   {       int wages;       public int Wages       {           get { return wages; }           set { wages = value; }       }   }   public class Manager : Worker   { }   class Program   {       public delegate void Handler(T obj);       public static void GetWorkerWages(Worker worker)       {           Console.WriteLine("Worker's total wages is " + worker.Wages);       }       public static void GetManagerWages(Manager manager)       {           Console.WriteLine("Manager's total wages is " + manager.Wages);       }       static void Main(string[] args)       {           HandlerworkerHander = new Handler(GetWorkerWages);           Worker worker = new Worker();           worker.Wages = 3000;           workerHander(worker);           HandlermanagerHandler = new Handler(GetManagerWages);

           Manager manager = new Manager();

           manager.Wages = 4500;

           managerHandler(manager);

       }

   }

事件

产生原因

事件是特殊的委托,他为委托提供了封装性,一方面允许从类的外部增加,删除绑定方法,另一方面又不允许从类的外部来触发委托所绑定了方法。

public delegate double PriceHandler();

    public class PriceManager

    {

        public PriceHandler GetPriceHandler;

        //委托处理,当价格高于100元按8.8折计算,其他按原价计算

        public double GetPrice()

        {

            if (GetPriceHandler.GetInvocationList().Count() > 0)

            {

                if (GetPriceHandler() > 100)

                    return GetPriceHandler()*0.88;

                else

                    return GetPriceHandler();

            }

            return -1;

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            PriceManager priceManager = new PriceManager();


            //调用priceManager的GetPrice方法获取价格

            //直接调用委托的Invoke获取价格,两者进行比较

            priceManager.GetPriceHandler = new PriceHandler(ComputerPrice);

            Console.WriteLine(string.Format("GetPrice\n  Computer's price is {0}!",

                priceManager.GetPrice()));

            Console.WriteLine(string.Format("Invoke\n  Computer's price is {0}!",

                priceManager.GetPriceHandler.Invoke()));


            Console.WriteLine();


            priceManager.GetPriceHandler = new PriceHandler(BookPrice);

            Console.WriteLine(string.Format("GetPrice\n  Book's price is {0}!",

                priceManager.GetPrice()));

            Console.WriteLine(string.Format("Invoke\n  Book's price is {0}!" ,

                priceManager.GetPriceHandler.Invoke()));


            Console.ReadKey();

        }

        //书本价格为98元

        public static double BookPrice()

        {

            return 98.0;

        }

        //计算机价格为8800元

        public static double ComputerPrice()

        {

            return 8800.0;

        }

    }

以上代码实现了对于100元以上商品的的88折处理。一方面为了给GetPriceHandler绑定方法就必须将委托声明为public,但是一旦声明为public

就可以在类外部直接通过Invoke来调用该委托所绑定的方法,而产生我们不需要的结果

当然我们可以将GetPriceHandler声明为private并且通过public 的addHandler,removeHandler来消除委托public的副作用,但是C#提供了更加优雅的方法:

那就是event关键字。

事件(event)可被视作为一种特别的委托,它为委托对象隐式地建立起add_XXX、remove_XXX 两个方法,用作注册与注销事件的处理方法。而且事件对应的变量成员将会被视为 private 变量,外界无法超越事件所在对象直接访问它们,这使事件具备良好的封装性,而且免除了add_XXX、remove_XXX等繁琐的代码。

public class EventTest

    {

        public delegate void MyDelegate();

        public event MyDelegate MyEvent;

    }

观察事件的编译过程可知,在编译的时候,系统为 MyEvent 事件自动建立add_MyEvent、remove_MyEvent 方法。

使用

事件能通过+=和-=两个方式注册或者注销对其处理的方法,使用+=与-=操作符的时候,系统会自动调用对应的 add_XXX、remove_XXX 进行处理。

值得留意,在PersonManager类的Execute方法中,如果 MyEvent 绑定的处理方法不为空,即可使用MyEvent(string)引发事件。但如果在外界的 main 方法中直接使用 personManager.MyEvent (string) 来引发事件,系统将引发错误报告。这正是因为事件具备了良好的封装性,使外界不能超越事件所在的对象访问其变量成员。

注意:在事件所处的对象之外,事件只能出现在+=,-=的左方。

public delegate void MyDelegate(string name);

    public class PersonManager

    {

        public event MyDelegate MyEvent;

        //执行事件

        public void Execute(string name)

        {

            if (MyEvent != null)

                MyEvent(name);

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            PersonManager personManager = new PersonManager();

            //绑定事件处理方法

            personManager.MyEvent += new MyDelegate(GetName);

            personManager.Execute("Leslie");

        }

        public static void GetName(string name)

        {

            Console.WriteLine("My name is " + name);

        }

    }

在绑定事件处理方法的时候,事件出现在+=、-= 操作符的左边,对应的委托对象出现在+=、-= 操作符的右边。对应以上例子,事件提供了更简单的绑定方式,只需要在+=、-= 操作符的右方写上方法名称,系统就能自动辩认。

public delegate void MyDelegate(string name);

    public class PersonManager

    {

        public event MyDelegate MyEvent;

        .........

    }

    class Program

    {

        static void Main(string[] args)

        {

            PersonManager personManager = new PersonManager();

            //绑定事件处理方法

            personManager.MyEvent += GetName;

            .............

        }

        public static void GetName(string name)

        {.........}

   }

如果觉得编写 GetName 方法过于麻烦,你还可以使用匿名方法绑定事件的处理。

public delegate void MyDelegate(string name);

    public class PersonManager

    {

        public event MyDelegate MyEvent;

        //执行事件

        public void Execute(string name)

        {

            if (MyEvent != null)

                MyEvent(name);

        }

        static void Main(string[] args)

        {

            PersonManager personManager = new      PersonManager();

            //使用匿名方法绑定事件的处理

            personManager.MyEvent += delegate(string name){

                Console.WriteLine("My name is "+name);

            };

            personManager.Execute("Leslie");

        }

    }

lamda表达式

在Framework 2.0 以前,声明委托的唯一方法是通过方法命名,从Framework 2.0 起,系统开始支持匿名方法。

通过匿名方法,可以直接把一段代码绑定给事件,因此减少了实例化委托所需的编码系统开销。

而在 Framework 3.0 开始,Lambda 表达式开始逐渐取代了匿名方法,作为编写内联代码的首选方式。总体来说,Lambda 表达式的作用是为了使用更简单的方式来编写匿名方法,彻底简化委托的使用方式。

使用匿名方法

public delegate void MyDelegate(string name);

public class PersonManager

{

  public event MyDelegate MyEvent;

  .........

}

static void Main(string[] args)

{

    PersonManager personManager = new PersonManager();

   //使用匿名方法绑定事件的处理

   personManager.MyEvent += delegate(string name){

   Console.WriteLine("My name is "+name);

    };

}

使用lambda表达式

static void Main(string[] args)

{

    PersonManager personManager = new PersonManager();

   //使用lambda表达式

   personManager.MyEvent += (name) =>{

   Console.WriteLine("My name is "+name);

    };

}

常用泛型委托:

Action此委托由系统提供 无需声明Action 支持0~16个参数,可以按需求任意使用。public delegate void Action()public delegate void Action(T1 obj1)public delegate void Action(T1 obj1, T2 obj2)public delegate void Action(T1 obj1, T2 obj2,T3 obj3)............public delegate void Action(T1 obj1, T2 obj2,T3 obj3,......,T16 obj16)static void Main(string[] args)        {            int x = 1000;            Action action = () => x = x + 500;            action.Invoke();            Console.WriteLine("Result is : " + x);        }static void Main(string[] args)        {            Action action = (x) =>

            {

                x = x + 500;

                Console.WriteLine("Result is : " + x);

            };

            action(1000);

        }

委托 Func 与 Action 相似,同样支持 0~16 个参数,不同之处在于Func 必须具有返回值public delegate TResult Func()public delegate TResult Func(T1 obj1)public delegate TResult Func(T1 obj1,T2 obj2)public delegate TResult Func(T1 obj1,T2 obj2,T3 obj3)............public delegate TResult Func(T1 obj1,T2 obj2,T3 obj3,......,T16 obj16)static void Main(string[] args)        {            Funcfunc = Account;            double result=func(1000, true);            Console.WriteLine("Result is : "+result);        }        static double Account(double a,bool condition)        {            if (condition)                return a * 1.5;            else                return a * 2;        }static void Main(string[] args){         Func func = (a,condition) =>

         {

             if (condition)

             {

                 return a * 1.5;

             }

             else

             {

                 return a * 2;

             }

         };

         double result=func(1000, true);

         Console.WriteLine("Result is : "+result);

 }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • 委托:其实就是一种命令,A委托B,做事情的就是B * 上面是在现实世界里,反过来在程序世界中的 * 委托实际上就是...
    Unity开发阅读 609评论 1 0
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • 简易线程控制方法: 新建线程,无返回值,不带参 Thread thread = new Thread(new Th...
    铸剑悄悄对你说阅读 848评论 1 0
  • 儿子 儿子识字很早,所以早早就能自己看书,不过大多时候他还是愿意和我一起看,看不懂的地方就让我给他讲。 他三岁的时...
    美生活阅读 290评论 2 4
  • 我要做一件事,需要两三个人。 清晨,提交最后一次结果,庆幸着比赛终于结束了。但是我知道心里一直有一个念头——训练一...
    佳乐change阅读 1,521评论 0 3