【问题标题】:Simple Delegate (delegate) vs. Multicast delegates简单委托(委托)与多播委托
【发布时间】:2011-01-12 15:05:25
【问题描述】:

我浏览了很多文章,但我仍然不清楚我们通常创建的普通委托和多播委托之间的区别。

public delegate void MyMethodHandler(object sender);
MyMethodHandler handler = new MyMethodHandler(Method1);
handler += Method2;
handler(someObject);

上面的委托 MyMethodHandler 会调用这两个方法。 现在多播委托从何而来。我读过他们可以调用多种方法,但恐怕我对委托的基本理解是不正确的。

【问题讨论】:

    标签: c# .net delegates multicastdelegate


    【解决方案1】:

    多播委托是引用多个函数的委托。当您调用多播委托时,该委托所指向的所有函数都会被调用。

    类型 1:

    0 参数和 void 返回类型委托 -

    方法一——

    using System;
    
    delegate void SampleDelegate ();    //A delegate with 0 argument and void     return type is declared
    
    class MainClass
    {
        public static void Main ()
        {
            SampleDelegate Del1 = new SampleDelegate (Message1);         //Del1 declared which points to function Message1
            SampleDelegate Del2 = new SampleDelegate (Message2);        //Del2 declared which points to function Message2
            SampleDelegate Del3 = new SampleDelegate (Message3);        //Del3 declared which points to function Message3
            SampleDelegate Del4 = Del1 + Del2 + Del3;                   //Del4 declared which points to function Message4
    
            //Del4 is then initialized as sum of Del1 + Del2 + Del3
    
            Del4 ();        //Del4 is invoked;
    
            //Del4 in turn invokes Del1, Del2 and Del3 in the same order they were initialized to Del4
            //Del1, Del2, Del3 in turn invokes their respective functions to which they point to
            //The three functions Message1, Message2 and Message3 gets executed one after another
    
        }
    
            //Output:
            //This is message 1
            //This is message 2
            //This is message 3
    
            Del4 - Del1;    //Removes Del1 from Del4
            Del4();           
    
            //New Output:
            //This is message 2
            //This is message 3
    
            Del4 + Del1;    //Again adds Del1 to Del4
            Del4();
    
            //New Output:
            //This is message 1
            //This is message 2
            //This is message 3
    
    
        public static void Message1 ()      //First sample function matching delegate signature
        {
            Console.WriteLine ("This is message 1");
        }
    
        public static void Message2 ()      //Second sample function
        {
             Console.WriteLine ("This is message 2");
        }
    
        public static void Message3 ()      //Third sample function
        {
            Console.WriteLine ("This is message 3");
        }
    }
    

    方法二 -

    using System;
    
    delegate void SampleDelegate ();
    
    class MainClass
    {
        public static void Main ()
        {
            SampleDelegate del = new SampleDelegate (Message1);         //Declares del and initializes it to point to method Message1
            del += Message2;                                        //Now method Message2 also gets added to del. Del is now pointing to two methods, Message1 and Message2. So it is now a MultiCast Delegate
            del += Message3;                                        //Method Message3 now also gets added to del
    
            del ();                                                 //Del invokes Message1, Message2 and Message3 in the same order as they were added
    
            /*
            Output:
            This is Message1
            This is Message2
            This is Message3
            */
    
            del -= Message1;                                        //Method     Message1 is now removed from Del. It no longer points to Message1
                                                                    //Del invokes the two remaining Methods Message1 and Message2 in the same order
            del ();
            /*
            New Output:
            This is Message2
            This is Message3
            */
    
            del += Message4;                                        //Method Message4 gets added to Del. The three Methods that Del oints to are in the order 1 -> Message2, 2 -> Message3, 3 -> Message4
                                                                    //Del invokes the three methods in the same order in which they are present.
            del ();
            /*
            New Output:
            This is Message2
            This is Message3
            This is Message4
            */
    
        }
    
        public static void Message1 ()
        {
            Console.WriteLine ("This is Message1");
        }
    
        public static void Message2 ()
        {
            Console.WriteLine ("This is Message2");
        }
    
        public static void Message3 ()
        {
            Console.WriteLine ("This is Message3");
        }
    
        public static void Message4 ()
        {
            Console.WriteLine ("This is Message4");
        }
    }
    

    类型 2:

    0 个参数和 int 返回类型委托

    方法1-

    using System;
    
    delegate int SampleDelagate ();
    
    class MainClass
    {
        public static void Main ()
       {
            SampleDelagate del1 = new SampleDelagate (Method1);
            SampleDelagate del2 = new SampleDelagate (Method2);
            SampleDelagate del3 = new SampleDelagate (Method3);
            SampleDelagate del4 = del1 + del2 + del3;
    
            int ValueReturned = del4 ();
    
            //Del4 invokes Del1, Del2, Del3 in the same order. Here the return type is int. So the return of last delegate del3 is returned. Del3 points to Method3. So returned value is 3.
    
            Console.WriteLine (ValueReturned);
    
            //Output: 3
        }
    
        public static int Method1 ()
        {
            return 1;
        }
    
        public static int Method2 ()
        {
            return 2;
        }
    
        public static int Method3 ()
        {
            return 3;
        }
    }
    

    方法2-

    与类型 1 相同的过程

    所以当有 MultiCast Delegate 的返回类型时,返回值是最后一个委托的返回值。

    类型 3:

    int、int、ref int 参数和 void 返回类型委托 -

    using System;
    
    delegate void SampleDelegate (ref int SampleReferenceParameter);
    
    class MainClass
    {
        public static void Main ()
        {
            SampleDelegate del1, del2, del3, del4;
            del1 = new SampleDelegate (SampleMethodOne);
            del2 = new SampleDelegate (SampleMethodTwo);
            del3 = new SampleDelegate (SampleMethodTwo);
            del4 = del1 + del2 + del3 - del3;
    
            int SampleReferenceParameterValue = 0;
            del4 (ref SampleReferenceParameterValue);
    
            Console.WriteLine (SampleReferenceParameterValue); 
        }
    
        public static void SampleMethodOne (ref int SampleReferenceParameter)
        {
            SampleReferenceParameter = 1;
        }
    
        public static void SampleMethodTwo (ref int SampleReferenceParameter)
        {
            SampleReferenceParameter = 2;
        }
    
        public static void SampleMethodThree (ref int SampleReferenceParameter)
        {
            SampleReferenceParameter = 3;
        }
    }
    
    /*
    Here del4 is first set as sum of del1, del2 and del3. Then del3 is subtracted from del4. So del4 now has del1, del2.
    
    When del4 is invoked, first del1 and then del2 is invoked.
    
    del1 sets reference parameter to 1. del2 sets reference parameter to 2.
    
    But since del2 is called last final value of reference parameter is 2
    */
    

    【讨论】:

      【解决方案2】:

      澄清一点:所有委托都是MulticastDelegate类的实例,无论它们是否有一个或多个目标方法。原则上,具有单个目标或多个目标的委托之间没有区别,尽管运行时针对具有单个目标的常见情况进行了一些优化。 (但不可能有 0 个目标的委托,它是一个或多个。)

      当您实例化像 new MyMethodHandler(Method1) 这样的委托时,您会创建一个具有单个目标的委托(Method1 方法)。

      具有多个目标的委托是通过组合两个现有委托来创建的。生成的委托将具有目标的总和。委托可以与Delegate.Combine() 显式组合,也可以通过在现有委托上使用+= 运算符来隐式组合,如您的示例所示。

      调用委托依次调用委托中的每个目标。因此,在您的示例中,handler(someObject); 将调用两个方法(Method1Method2),因为您已经创建了具有这两个目标的委托。

      【讨论】:

        【解决方案3】:

        C# 规范规定所有委托类型都必须可转换为System.Delegate。事实上,实现的方式是所有的委托类型都派生自System.MulticastDelegate,而System.MulticastDelegate又派生自System.Delegate

        清楚吗?我不确定这是否回答了您的问题。

        【讨论】:

        • 是的,埃里克。通常,当我向人们(或人们问我)询问委托时,我们通常会说委托有两种类型——单播和多播。现在我知道只有一个像“委托”这样的东西,它可以是单播或多播,具体取决于它包含的方法引用的数量。
        【解决方案4】:

        很抱歉添加到其他人的答案,但我认为代表是按照添加顺序调用的。

        “多播代表”部分

        http://msdn.microsoft.com/en-us/library/orm-9780596527570-03-04.aspx

        【讨论】:

        • 是的,如果有返回类型,则返回最后一个函数的结果 - 很奇怪。
        【解决方案5】:

        “所有委托实例都具有多播功能。” - http://msdn.microsoft.com/en-us/library/orm-9780596527570-03-04.aspx

        “在 C# 中,所有委托类型都支持多播” - http://msdn.microsoft.com/en-us/library/orm-9780596516109-03-09.aspx

        【讨论】:

        • any 委托实例如何能够调用任何数量的操作,而不是它创建的数量?如果创建一个委托来调用三件事,我认为委托实例将始终做三件事。同样,如果它是为了做一件事而创建的。如果创建一个委托只是为了做一件事,那么该实例怎么能做更多呢?
        • @supercat,它没有。
        • 那么“所有委托实例都具有多播能力”是什么意思?当然,所有的委托类型都有这样的能力,并且接受委托而不是事件订阅的代码必须准备好让它成为一个多播委托(如果一个事件在内部使用多播委托,则传递一个多播委托给 Add 方法,然后将其传递给 Remove 可能实际上不会取消订阅事件;默认事件处理程序以这种方式出现故障意味着事件不应该容忍多播事件。
        【解决方案6】:

        This article 解释得很好:

        delegate void Del(string s);
        
        class TestClass
        {
            static void Hello(string s)
            {
                System.Console.WriteLine("  Hello, {0}!", s);
            }
        
            static void Goodbye(string s)
            {
                System.Console.WriteLine("  Goodbye, {0}!", s);
            }
        
            static void Main()
            {
                Del a, b, c, d;
        
                // Create the delegate object a that references 
                // the method Hello:
                a = Hello;
        
                // Create the delegate object b that references 
                // the method Goodbye:
                b = Goodbye;
        
                // The two delegates, a and b, are composed to form c: 
                c = a + b;
        
                // Remove a from the composed delegate, leaving d, 
                // which calls only the method Goodbye:
                d = c - a;
        
                System.Console.WriteLine("Invoking delegate a:");
                a("A");
                System.Console.WriteLine("Invoking delegate b:");
                b("B");
                System.Console.WriteLine("Invoking delegate c:");
                c("C");
                System.Console.WriteLine("Invoking delegate d:");
                d("D");
            }
        }
        /* Output:
        Invoking delegate a:
          Hello, A!
        Invoking delegate b:
          Goodbye, B!
        Invoking delegate c:
          Hello, C!
          Goodbye, C!
        Invoking delegate d:
          Goodbye, D!
        */
        

        【讨论】:

        • 多播委托仅仅是在其调用列表中有多个方法引用的普通委托吗?
        • 没错。多播委托将调用多个方法。
        • .NET 中的代表 多播代表(据我所知)。无论您选择将零个或一个或多个处理程序附加到它们,它们仍然是多播委托。
        • 请务必注意,多播委托会以不可预知的顺序调用其订阅者。不要假设它们会以任何特定的顺序被调用。
        • @MikeChristian 获得了“多播代表将以不可预测的顺序呼叫其订阅者”的链接? 15.3的第二段好像说是按顺序调用的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多