DotNet委托小结

.net委托小结

看了CLR Via C#的委托一章,有些新收获,做一下总结。

一.认识委托

委托也是.net的一种类型,它可以包装与它签名相同的函数,对函数进行注册或删除,然后可以向调用函数本身一样执行,多播委托则按顺序调用函数。

委托的定义如下: delegate 返回值类型 委托名称(参数)

委托可以定义在类的内部或定义为全局,返回值类型不能为值类型(int,double等,具体原因不太清楚),否则编译器会报错。

查看IL可以发现,委托被定义为一个类,继承自MultiCastDelegate。先看它的构造函数,有2个参数objectmethod,(1)如果被包装函数是static,objectnull,instance的,则指向该实例;(2)method为包装的函数。然后看invoke方法,可以看出,它具有和被包装函数同样的签名,这个函数是最终被委托执行的函数。

二.多播委托

委托默认都是多播的,把函数注册到委托对象的方法是combine,注销的方法是remove,都是静态的。

delegate void FeedBack(string str);

   Void function1(string str){//};

Void function2(string str){//};

FeedBack fb = null;

fb = FeedBack.Combine(new FeedBack(function1)) as FeedBack;

fb = FeedBack.Combine(new FeedBack(function2)) as FeedBack;

//remove function2

fb = FeedBack.remove(fb,new FeedBack(function2)) as FeedBack;

      也可以用重载符号+,-来完成注册和注销。

      多播委托调用时的执行顺序是按照注册的顺序执行的,委托对象内有个数组

来维护。

    多播委托有些问题,例如:函数如果有返回值,将不能得到;还有,如果其中间某个函数执行发生了异常,其后面的函数将不再执行。

  如何解决多播委托的这一问题呢,也就是如何能显示的控制对函数的调用,有一个方法,MultiCastDelegate有个方法叫GetInvactionList(),它返回一个

Delegate数组,这个数组对应包装的函数,对其进行循环迭代,可以显示的控制函数的调用,并且处理中间发生的异常。

例子代码如下:

 

using System;

using System.Collections.Generic;

public delegate string GetStatus();

 

    /// <summary>

    /// MultiCastDelegate类的GetInvocationList()函数可得到Delegate数组,从而可以显示的控制对多播委托的调用。

    /// </summary>

    class Program

    {

        static void Main(string[] args)

        {

            GetStatus getStatus = null;

 

            getStatus += new GetStatus(new Light().SwitchPosition);

            getStatus += new GetStatus(new Fan().Speed);

            getStatus += new GetStatus(new Speaker().Volume);

 

            Console.WriteLine(GetComponentsStatusReport(getStatus));

 

        }

 

        public static string GetComponentsStatusReport(GetStatus getStatus)

        {

            if (getStatus == null)

            {

                return null;

            }

            StringBuilder report = new StringBuilder();

            Delegate[] arrayOfDelegates = getStatus.GetInvocationList();

 

            foreach (GetStatus g in arrayOfDelegates)

            {

                try

                {

                    report.AppendFormat("{0}{1}{1}", g(), Environment.NewLine);

                }

                catch (InvalidOperationException e)

                {

                    object target = getStatus.Target;

                    report.AppendFormat("Failed to get status from {1}{2}{0} Error: {3}{0}{0}", Environment.NewLine,

                        ((target == null) ? "" : target.GetType() + "."), getStatus.Method.Name, e.Message);

                }

            }

 

 

            return report.ToString();

 

        }

    }

 

    internal sealed class Light

    {

        public string SwitchPosition()

        {

            return "The light is off";

        }

    }

 

    internal sealed class Fan

    {

        public string Speed()

        {

            throw new InvalidOperationException("The fan broke due to overheating");

        }

    }

 

    internal sealed class Speaker

    {

        public string Volume()

        {

            return "The volume is loud";

        }

    }

执行结果输出:

The light is off

Failed to get status from MultiCastDelegateDemo.Speaker.Volume

 Error: The fan broke due to overheating

The volume is loud

三.Dotnet对委托的简化操作

1.  匿名函数,:

this.button1.Click += delegate(object send, EventArgs ev) { MessageBox.Show("Anonymous Delegate with parameters! "); };

2.  如果函数不使用参数,可以省略,:

this.button1.Click += delegate { MessageBox.Show("Anonymous Delegate without parameters!"); };

四.反射与委托

对于在编译时,不知道委托类型和函数类型时,可以使用反射来做。Delegate类有CreateDelegate()的多种重载形式,用反射来创建Delegate对象,如

CreateDelegate(Type type,MethodInfo method),type为委托定义类型,method为包装函数,得到Delegate对象后,有个DynamicInvoke(参数)函数,可以动态执行调用。

例子如下:

internal delegate object ToInt32s(Int32 n1,Int32 n2);

    internal delegate object OneString(string s);

 

    class Program

    {

        static void Main(string[] args)

        {

            if (args.Length < 2)

            {

                string fileName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location);

                string usage = @"Usage:" +

                                "{0}{1} delType methodName [Arg1] [Arg2]" +

                                "{0}    where delType must be TwoInt32s or OneString" +

                                "{0}    if delType is ToInt32s,methodName must be Add or Sutract" +

                                "{0}    if delType is OneString,methodName must be NumChars or Reverse" +

                                "{0}    " +

                                "{0}Examples:    " +

                                "{0}    {1}  ToInt32s Add 123,321" +

                                "{0}    {1}  ToInt32s Subtract 321,123" +

                                "{0}    {1}  OneString NumChars /"hello /"there " +

                                "{0}    {1}  OneString Reverse /"hello /"there ";

                Console.WriteLine(usage, Environment.NewLine, fileName);

                return;

            }

 

            Type delType = Type.GetType(args[0]);

 

            if (delType == null)

            {

                Console.WriteLine("Invalid type argument:" + args[0]);

                return;

            }

 

            Delegate d;

 

            try

            {

                MethodInfo mi = typeof(Program).GetMethod(args[1], BindingFlags.NonPublic | BindingFlags.Static);

                d = Delegate.CreateDelegate(delType, mi);

            }

            catch (ArgumentException e)

            {

                Console.WriteLine("Invalid methodName argument:" + args[1]);

                return;

            }

 

            object[] callbackArgs = new object[args.Length - 2];

            if (d.GetType() == typeof(ToInt32s))

            {

                try

                {

                    for (int i = 2; i < args.Length; i++)

                    {

                        callbackArgs[i - 2] = int.Parse(args[i]);

                    }

                }

                catch (FormatException e)

                {

                    Console.WriteLine("Parameters must be integer");

                    return;

                }

            }

 

            if (d.GetType() == typeof(OneString))

            {

                Array.Copy(args, 2, callbackArgs, 0, callbackArgs.Length);

            }

 

            try

            {

                object result = d.DynamicInvoke(callbackArgs);

                Console.WriteLine("Result="+result);

            }

            catch(TargetParameterCountException)

            {

                Console.WriteLine("Incorrect number of parameters specified.");

            }

        }

 

        private static object Add(Int32 n1, Int32 n2)

        {

            return n1 + n2;

        }

 

        private static object Subtract(Int32 n1, Int32 n2)

        {

            return n1 - n2;

        }

 

        private static object NumChars(string s)

        {

            return s.Length;

        }

 

        private static object Reverse(string s)

        {

            Char[] chars = s.ToCharArray();

            Array.Reverse(chars);

            return chars.ToString();

        }

  

配置参数如下:ToInt32s Add 123 321,得到的结果为:result=444

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值