当前位置:编程学习 > C#/ASP.NET >>

事件的写法是不是错了

从某书上看到的代码:
         public delegate void DisconnectedHandler(object sender);
        public event DisconnectedHandler Disconnected;

        private void OnDisconnected(object sender)
        {
    MessageBox.Show("123456");
        }

        public void Form1()
        {
    //Disconnected += new DisconnectedHandler(OnDisconnected);
    Disconnected(this);
        }
为什么是:
Disconnected(this);
而不是:
Disconnected += new DisconnectedHandler(OnDisconnected);

如果没错,Disconnected(this); 的作用是什么?谢谢 delegate --------------------编程问答--------------------  Form1是事件的发布者,哪有自己发布自己订阅的 --------------------编程问答-------------------- Disconnected是一个你定义的事件,初学时,你把它想象成函数

Disconnected(this);就是调用这个函数 --------------------编程问答-------------------- Disconnected += new DisconnectedHandler(OnDisconnected);//外部定义
是告诉我们的程序监听到事件后要如何处理


Disconnected(this);//内部触发
是告诉我们的程序有事件发生了
--------------------编程问答-------------------- 给你做个比喻  你声明的委托相当于一个空子弹链
Disconnected += new DisconnectedHandler(OnDisconnected);
的过程相当于往子弹链上添上一颗子弹  子弹就是OnDisconnected这个方法
Disconnected(this)
的过程就相当于将你这个子弹链上的子弹依次打出去  也就是依次执行你这个委托链上的方法不知道这样比喻你能懂不 --------------------编程问答--------------------
引用 1 楼 u010383116 的回复:
 Form1是事件的发布者,哪有自己发布自己订阅的

Form1这几个字符是我随便写的 --------------------编程问答--------------------
引用 4 楼 gdream2008 的回复:
给你做个比喻  你声明的委托相当于一个空子弹链
Disconnected += new DisconnectedHandler(OnDisconnected);
的过程相当于往子弹链上添上一颗子弹  子弹就是OnDisconnected这个方法
Disconnected(this)
的过程就相当于将你这个子弹链上的子弹依次打出去  也就是依次执行你这个委托链上的方法不知道这样比喻你能懂不


Disconnected(this);这条语句没有关联到子弹(方法)OnDisconnected啊,哪有子弹打出去? --------------------编程问答-------------------- Disconnected(this);就是按钮 一按 OnDisconnected 就打出去了 OnDisconnected就被触发了 OnDisconnected就是子弹 是连发的 就是说 多播 --------------------编程问答-------------------- Disconnected += new DisconnectedHandler(OnDisconnected); 这句话不就是关联么 --------------------编程问答-------------------- 楼上,没这句:
Disconnected += new DisconnectedHandler(OnDisconnected);

代码:
         public delegate void DisconnectedHandler(object sender);
        public event DisconnectedHandler Disconnected;

        private void OnDisconnected(object sender)
        {
    MessageBox.Show("123456");
        }

        public void Form1()
        {
    Disconnected(this);
        } --------------------编程问答-------------------- 没那句你的事件 执行了跟没执行一样 --------------------编程问答--------------------
楼上讲的对
        public void Form1()
        {
//下面两行都要有
    Disconnected += new DisconnectedHandler(OnDisconnected);
    Disconnected(this);
        }
当然Disconnected(this);可以放在其它方法中

以上的方法都过时了,在Net.Framework3.5中提倡使用泛型委托EventHandler<T>,经过几天的努力,写出了一个通用的泛型委托类
        private void OnDisconnected(object sender)
        {
    MessageBox.Show("123456");
        }

        public void Form1()
        {
    //使用泛型委托EventHandler<T>
                HasEvent he = new HasEvent();
                he.SampleEvent += new EventHandler<MyEventArgs>(OnDisconnected);
                he.DemoEvent(this );
        }

    //定义允许事件将对象(类、字符串等)回传事件处理程序的类,用在泛型委托(自定义(预定义)委托)EventHandler<T>。
    public class MyEventArgs : EventArgs
    {
        private object msg;

        public MyEventArgs(object messageData )
        {
            msg = messageData;
        }

        public object Message
        {
            get { return msg; }
            set { msg = value; }
        }
    }

    //---------------------------------------------------------
    //定义事件类,用在泛型委托(自定义(预定义)委托)EventHandler<T>。
    public class HasEvent
    {
        public event EventHandler<MyEventArgs> SampleEvent;

        public void DemoEvent(object val)
        {
            EventHandler<MyEventArgs> temp = SampleEvent;
            if (temp != null)
                temp(this, new MyEventArgs(val));
        }
    } --------------------编程问答--------------------
引用 楼主 tslt65 的回复:
从某书上看到的代码:
         public delegate void DisconnectedHandler(object sender);
        public event DisconnectedHandler Disconnected;

        private void OnDisconnected(object sender)
        {
    MessageBox.Show("123456");
        }

        public void Form1()
        {
    //Disconnected += new DisconnectedHandler(OnDisconnected);
    Disconnected(this);
        }
为什么是:
Disconnected(this);
而不是:
Disconnected += new DisconnectedHandler(OnDisconnected);

如果没错,Disconnected(this); 的作用是什么?谢谢

请问这是什么烂书呢?

写了代码又注释掉,可见这本书的作者是个垃圾小程序员。

傻B都懂得道理“既然是注释掉的代码你就可以删除掉了”。那么你删除掉注视代码之后,既然Main中的代码根本与事件无关,那么整个程序就剩下了
public delegate void DisconnectedHandler(object sender);
        public event DisconnectedHandler Disconnected;

        public void Form1()
        {
            Disconnected(this);
        }

你再看这剩下的代码。

不要纠结这里的代码,不要再读这个作者写的书! --------------------编程问答--------------------
引用 5 楼 tslt65 的回复:
Form1这几个字符是我随便写的

难道你是篡改了人家作者的代码,然后再来说人家的代码有问题? --------------------编程问答-------------------- 《C#线程参考手册》,楼上没看过吗?
修正一下11#的方法,少了一个参数
        private void OnDisconnected(object sender, MyEventArgs e)
        {
    MessageBox.Show("123456");
        } --------------------编程问答-------------------- 事件重点事要知道他是怎么来的(其实就是委托)为什么不用委托而用事件(安全性),主要什么时候要用事件,哪些地方最好不要事件(垃圾回收),否则今天学了事件的画法,三天后又忘记了 --------------------编程问答-------------------- 楼上说到点子上了,我正被这个问题困扰,请给于赐教啊 --------------------编程问答-------------------- #15 我正被这个问题困扰,请给于赐教啊  --------------------编程问答-------------------- 举个栗子;

让你设计Button类;
实现Click事件;

1.你不知道用户想在Click里执行什么;
2.用户不知道Click事件什么时候触发;

反过来说;

1.你实现Click事件,只要在适当的时候触发它(Disconnected(this))
2.用户使用Click事件只需要注册事件处理函数

MyClass my = new MyClass();
my.Disconnected += new DisconnectedHandler(my_Disconnected);


还有几点需要说明:

1.你的OnDisconnected有这种用法,但是都是写成virtual
2.不应该直接公开事件,而应该用add和remove包装一层
3.触发事件Disconnected(this),需要判断是否为空

DisconnectedHandler tmp = System.Threading.Interlocked.CompareExchange(
    ref Disconnected, null, null)
if (tmp != null) tmp(this);

   --------------------编程问答-------------------- 嗯,楼上#18的建议我好好琢磨琢磨 --------------------编程问答-------------------- 这里有个例子,对理解事件委托和有返回值的委托有帮助,例子使用泛型委托方式

    //定义有返回值的委托 
    public delegate string GenricDelegate<T, S>(T title, S author);

    //定义事件委托。
    public delegate void GenricDelegateEnent<E,P>(E Name,P Address);

    public class GenericDelegateClass<V,F>
    {
        //声明委托
        public GenricDelegate<V, F> GdeleValue;
        //声明事件委托
        public event GenricDelegateEnent<V, F> GdEvent = null;

        public string GetValues(V title, F author)
        {
            //调用委托
            return GdeleValue(title, author);
         }

        public GenericDelegateClass()
        {
         }

        public void InvokeEvent(V name, F address)
        {
            if (GdEvent != null)
            {
                //调用委托
                 GdEvent(name, address);
             }
         }
     }
上面我们定义及调用了泛型委托,接下来就来梆定委托。

        private void btnDelegate_Click(object sender, EventArgs e)
        {

             GenericDelegateClass<string, string> gd = new GenericDelegateClass<string, string>();
            //将DelegateReturn事件梆定给GdeleValue
             gd.GdeleValue = new GenricDelegate<string, string>(DelegateReturn);
            //将GenericEvent事件梆定给GdEvent
             gd.GdEvent += new GenricDelegateEnent<string, string>(GenericEvent<string,string>);
         }

        public string DelegateReturn<T,S>(T title,S author)
        {
            return title.ToString() + author;
         }

        private void GenericEvent<V, F>(V name, F address)
        {
            //
         }
在这里我们看到我在梆定DelegateReturn的时候并没有带泛型参数。在这里的泛型参数其实是没什么意义的。因为他的类型取决于调用委托的方法的类型。也就是在前面那段代码中InvokeEvent方法的类型,这里的DelegateReturn要用泛型方法是可以随时跟InvokeEvent的参数类型保持一至。这样梆定后我们再来调用gd.GetValues("my generic post","fastyou");这样调用的其实就是DelegateReturn的方法,这就是委托的好处了,同样调用gd.InvokeEvent("my generic post","fastyou");就是GenericEvent方法。
--------------------编程问答-------------------- [转]c#中event内幕(add与remove) 
代码:
public event EventHandler<NewMailEventArgs> NewMail;

C#编译器编译这行代码时,会把它翻译成以下3个构造:

//1. 一个被初始化为null的私有委托字段
private EventHandler<NewMailEventArgs> NewMail = null;

//2. 一个允许对象订阅事件的公共方法add_Xxx(其中Xxx是事件的名称)
[MethodImpl(MethodImplOptions.Synchronized)]
public void add_NewMail(EventHandler<NewMailEventArgs> value) {

    NewMail = (EventHandler<NewMailEventArgs>)
    Delegate.Combine(NewMail, value);
}

//3. 一个允许对象注销事件的公共方法remove_Xxx(其中Xxx是事件的名称)
[MethodImpl(MethodImplOptions.Synchronized)]
public void remove_NewMail(EventHandler<NewMailEventArgs> value) {
    NewMail = (EventHandler<NewMailEventArgs>)
    Delegate.Remove(NewMail, value);
}

第一个构造只是一个适当的委托类型的字段。这个字段引用的是一个委托链表的首部,当事件发生时,链表中的委托对象将被通知。该字段被初始化为null,这意味着刚开始没有监听者订阅这个事件。当有方法订阅事件时,该字段会指向一个EventHandler <NewMailEventArgs>委托的实例,该实例还可以引用额外的EventHandler <NewMailEventArgs>委托。当监听者订阅了事件时,它只需将一个委托类型的实例添加到该委托类型的链表上即可。显然,注销事件意味着从委托链表上移除相应的委托实例。

注意,委托字段,即本例中的NewMail,尽管在源代码中将事件定义为public,但委托字段也总是为private。将委托字段定义为私有方式可以防止类定义外的代码错误地操作该字段。如果该字段为公共字段,那么任何代码都可以改变字段的值,从而删除所有已订阅事件的委托实例。

C#编译器产生的第二个构造是一个允许其他对象订阅事件的方法。C#编译器通过在事件名称 (NewMail)前添加add_自动地命名方法。C#编译器还自动地为方法产生代码。产生的代码通常调用System.Delegate的静态 Combine方法,并将委托实例添加到委托链表上,然后返回新的链表首部(得到已保存到字段中的内容)。 
当C#编译器看到使用-=操作符注销事件委托的代码时,会生成一个对事件的remove方法的调用。

mm.remove_NewMail(new EventHandler<NewMailEventArgs>(FaxMsg));

和+ =操作符一样,即使使用的编程语言不直接支持事件,我们仍然可以通过显式地调用remove访问器方法来注销事件。remove方法通过扫描链表,寻找传入的密封了相同方法的委托来注销事件的委托。如果找到匹配的委托,就将其从事件的委托链表上移除。如果没有找到,也不会出现任何错误,事件的委托链表也不会有任何改变。
顺便说一下,C#要求代码使用+=和-=操作符在链表上添加和移除委托。如果我们试图显式地调用add和remove方法,那么C#编译器将生成一个错误消息:CS0571: “不能显式调用操作符或访问器方法”。 

C# 编译器为事件的add和remove方法增加[MethodImpl (MethodImplOptions.Synchronized)]属性。这个属性的目的是为了确保在操作实例的事件成员时,对于任何一个对象,在同一时刻只能有一个add方法或者remove方法可以执行。该属性同样确保在操作静态事件成员时,同一时刻也只能有一个add方法或者remove方法可以执行。这里需要线程同步,以免委托对象的链表被破坏。但应注意,CLR实现线程同步的方式中存在许多问题。

对实例(非静态)方法应用 MethodImpl属性时,CLR使用对象本身作为线程同步锁。这意味着如果类定义了许多事件,那么所有的add和remove方法都将使用相同的锁,这种情况下,如果有多个线程同时订阅和注销不同的事件,则会损害可扩展性。但这种情况非常少见,而且对于大多数应用程序,这并不是一个问题。但是,线程同步的指导方针中规定方法不应在对象本身上加同步锁,因为同步锁将对所有的代码公开。这意味着任何人都可以编写代码锁住这个对象,从而可能导致其他线程死锁。如果希望自己编写的类型防御措施好,更加健壮,那么应使用不同的对象来完成加锁功能。10.5节将示范如何实现这个                功能。

在静态方法上应用[MethodImpl(MethodImplOptions.Synchronized)]属性时,CLR使用类型对象作为线程同步锁。这又意味着如果类定义了许多静态事件,那么所有的add和remove方法都将使用相同的锁,这种情况下,如果有多个线程同时订阅和注销不同的事件,则会损害代码的性能。但是这种情况也非常少见。

但还有一个严重的问题:线程同步指导方针指出,方法永远不要在类型对象上加锁,因为这个锁将对所有的代码公开。另外,当类型加载与域无关时,CLR中还有一个错误。这种情况会导致同步锁被使用该类型的所有应用程序域共享,如此一来,一个程序域中的代码会影响另一个应用程序域中运行的代码。实际中,C#编译器在实现静态add和remove方法的线程安全时应采用完全不同的方法。10.5节将讨论一种修正C#编译器这一缺陷的机制。

C#和CLR允许定义一个有一个或者多个实例 (非静态)事件成员的值类型(结构)。但必须意识到,在上述情况下,C#编译器根本不会保证线程安全。因为拆箱(unboxing)的值类型没有与其相关联的锁对象。实际上,C#编译器不为add和remove方法生成[MethodImpl (MethodImplOptions.Synchronized)]属性,因为该属性对值类型的实例方法没有效果。遗憾的是,当实例事件定义为值类型的成员时,的确没有更好的方式为它们保证线程安全。因此,建议大家尽量避免这样做。注意,在值类型中定义的静态事件(加上前面讨论的限制)可以保证线程安全,因为静态事件对类型对象(引用类型)本身加锁。但是,如果要使代码健壮,应采用10.5节讨论的机制。

有时我们会感到编译器生成的 add和remove方法不是那么理想。例如10.4节中讨论的使用MicrosoftC#编译器时遇到的所有线程安全问题。实际上,Microsoft 的C#编译器在安全编程(defensive coding)和健壮性方面永远不是最安全的。为了创建一个坚固的组件,建议经常采用本节介绍的技术,该技术可以用于解决与线程安全相关的所有问题。而且该技术同样也可以应用于其他目的。例如,显式实现add和remove方法的普遍原因就是类型定义了许多事件,而且又需要高效地进行存储。有关详情请参见 10.6节。

幸亏C#编译器以及其他许多编译器都允许开发人员显式地实现add和remove访问器方法。为了保证MailManager对象上事件的订阅和注销的线程安全,我们修改了MailManager类的代码,修改后的代码如下所示:


internal class MailManager {
    //创建一个作为线程同步锁的私有实例字段
    private readonly Object m_eventLock = new Object();

    //增加一个引用委托链表头部的私有字段
    private EventHandler<NewMailEventArgs> m_NewMail;

    //为类增加一个事件成员
    public event EventHandler<NewMailEventArgs> NewMail {
        //显式实现'add'方法
        add {
            //加私有锁,并向委托链表增加一个处理程序(以'value'为参数)
            lock (m_eventLock) { m_NewMail += value; }
        }
        //显式实现'remove'方法
        remove {
            //加私有锁,并从委托链表从中移除处理程序(以'value'为参数)
            lock (m_eventLock) { m_NewMail -= value; }
        }
    } 

补充:.NET技术 ,  C#
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,