为什么事件需要委托?我们为什么需要事件?

5
在过去的几周里,我一直对事件感到困惑。我知道代理(delegate)是如何工作的,但不是很清楚它的详细工作原理,只知道delegate datatype是单一委托(single cast delegate),而delegate void则是多重委托(multicast delegate)- 是指方法引用的列表。
我知道代理类型编译成一个类,但不幸的是,我仍然不确定方法是如何引用的。比如说:
delegate void TestDelegate();
TestDelegate testDelegate = new TestDelegate(myObject.SomeMethod) ;

问题1:我认为myObject是目标对象,SomeMethod是要引用的方法,但我只传递了一个输入参数。所以myObject.SomeMethod是否编译成字符串,然后由句点进行分割?这个想法很荒谬。

问题2:当您向多播委托添加内容时

multicastdelegate+=newmethodtobereference
multicastdelegate() ;

委托列表中的每个方法都会被调用或通知吗? 如果是这样,为什么我需要事件或event关键字呢?是为了告诉开发人员,“嘿,这是一个事件”吗?因为我真的很困惑,我只想进入下一阶段。这是我今天编写的一个示例代码,以测试是否需要事件关键字。

using System;
namespace LambdasETs
{
    public delegate void IsEvenNumberEventHandler(int numberThatIsEven);

    public class IsEvenNumberFound
    {
        public  IsEvenNumberEventHandler IsEvenNumberEvent;
        private int number;

        public void InputNumber(int n)
        {
            if(number %2 ==0)
            {
                if (IsEvenNumberEvent != null)
                {
                    IsEvenNumberEvent(n);
                }
            }
        }


        public static void Main()
        {
            IsEvenNumberFound isEvenNumberFound = new IsEvenNumberFound();

            isEvenNumberFound.IsEvenNumberEvent += IsEvenNumberAction;

             isEvenNumberFound.InputNumber(10);

            Console.ReadLine();

        }

        public static void IsEvenNumberAction(int number)
        {
            Console.WriteLine("{0} is an even number!", number);
        }
    }


}

将事件关键字添加到字段public IsEvenNumberEventHandler IsEvenNumberEvent;中没有任何区别。 请有人解释一下,以便新手可以理解,谢谢。

可能是重复的问题:事件不是字段 - 我不明白 - Ben Voigt
添加以下代码行 isEvenNumberFound.IsEvenNumberEvent = null;,现在尝试使用 event 关键字。 - Ben Voigt
@BenVoigt 它们都会抛出异常吗? - Lews Therin
2个回答

7
一个事件是委托的访问器,就像属性是字段的访问器一样。目的基本相同,它防止其他代码干扰委托对象。例如,当许多你不知道的代码已经订阅了回调时,将其设置为null。 事件仅限于使用+=和-=运算符添加和删除事件处理程序,外部代码无法访问私有委托对象。
要使用add and remove accessors自定义订阅,你通常不这样做,因为通常满意编译器生成的默认访问器,包括存储委托的隐藏后备字段。但在框架代码中,这并不罕见。例如,System.Windows.Forms.Control支持的许多事件处理程序都存储在单个EventHandlerList中。或者 WPF 中的等效物EventHandlersStore。

如果是这样,为什么我的编译器会抱怨不一致的无法访问性呢?属性的可访问性并不取决于它们所引用的字段,但似乎事件却很奇怪。 - Lews Therin
@LewsTherin:属性的访问权限应该与属性类型一致。事件和字段也是如此。 - Ben Voigt
@BenVoigt 我想我明白了你的意思。如果一个字符串是私有的,那么返回该字符串的属性也需要是私有的吗?如果它必须是公共的,因为事件必须是公共的... HansPassant说“它防止其他代码对委托对象进行干扰”是什么意思?我原以为是更改委托的签名.. 显然不是这样。 - Lews Therin
@Lews:类型System.String是公共的,因此您可以使用该类型具有公共属性。但是,如果您使用自己的类类型MyInternalType,则可以拥有私有和内部字段、内部属性等,但不能使用该类型具有公共字段或属性。 - Ben Voigt
@BenVoigt 那么这种情况下事件代表的是哪个领域? - Lews Therin

4
足够了解委托数据类型是单个转换委托。delegate void是多路广播委托 - 一系列对方法的引用。
并不是真的。即使具有非void返回类型,所有“正常”委托都是多路广播的。
问题1:我认为myObject是目标,SomeMethod是要引用的方法,但我只传递了一个输入。所以myObject.SomeMethod编译为字符串,并且字符串由句点分隔吗?荒谬的我知道。
不是,myObject.SomeMethod是一个方法组。这种委托实例创建方式涉及一些编译器魔法。
multicastdelegate += newmethodtobereference 如果multicastdelegate是普通委托变量,则相当于multicastdelegate = multicastdelegate + newmethodtobereference,即它创建一个调用多个方法的新委托,并将其分配给multicastdelegate。
现在来回答你的主要问题:事件的目的是什么?
事件具有委托类型。它们的行为类似于属性。它们的目的是封装,特别是它们只允许消费者订阅(+=)和取消订阅(-=),但不能读取事件的值。
属性是两种方法的组合:get和set。
事件是两个公共方法subscribe和unsubscribe的组合,在类似于字段的事件中还具有类似私有getter的东西。

对不起,myObject.SomeMethod 中的方法组是什么?“它创建一个调用多个方法的新委托。”我从未这样考虑过。想象一下,委托就像是指向其他委托列表的指针,可以充当指向其他委托列表的指针......我的上帝,多么复杂。 - Lews Therin
委托是不可变的。我认为,在单个委托上有优化,而在多个委托上使用数组,但所有委托类型都派生自“MultiCastDelegate”。 - CodesInChaos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接