Unity3d-C#之Delegate和Event疑惑?

1.如果多次注册同一个事件处理函数时,触发时处理函数是否也会多次触发?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestCSharp
{
    class Animation
    {
        public delegate void PlayHandler();
        public event PlayHandler Callback;

        public Animation()
        {
            this.Callback += Move;
            this.Callback += Move;
            this.Callback += Move;
            this.Callback();
        }

        public void Move()
        {
            Console.WriteLine("Move");
        }

        public void Run()
        {
            Console.WriteLine("Run");
        }

        public void Jump()
        {
            Console.WriteLine("Jump");
        }


    }
}


可以注册多次同一个函数,触发时会如上图所示执行多次.


2.如果注册了一个事件处理函数,却执行了两次或多次”注销事件“,是否会报错?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestCSharp
{
    class Animation
    {
        public delegate void PlayHandler();
        public event PlayHandler Callback;

        public Animation()
        {
            this.Callback += Move;
            this.Callback -= Move;
            this.Callback -= Move;
            this.Callback();
        }

        public void Move()
        {
            Console.WriteLine("Move");
        }

        public void Run()
        {
            Console.WriteLine("Run");
        }

        public void Jump()
        {
            Console.WriteLine("Jump");
        }


    }
}

注册一次,可以注销多次,但是要注意触发时要检查是否为空,做如下修改后避免如上图的空指针异常

            if (this.Callback != null)
            {
                this.Callback();
            }

3.如何认定两个事件处理函数是一样的? 如果是匿名函数呢?

using System.Text;

namespace TestCSharp
{
    class Animation
    {
        public delegate void PlayHandler();
        public event PlayHandler Callback;

        public Animation()
        {
            this.Callback += ()=> Console.WriteLine("Jump");
            //Event Unsubscription Via Anonymous Delegate
            this.Callback -= () => Console.WriteLine("Jump");
        }

    }
}

Resharper给出提示Event Unsubscription Via Anonymous Delegate,看起来一致的匿名函数,实际上方法签名是不一样,所以不能如上代码所示取消订阅一个匿名方法。如下代码给出如何取消订阅匿名方法的解决方案。

using System.Text;

namespace TestCSharp
{
    class Animation
    {
        public delegate void PlayHandler();
        public event PlayHandler Callback;

        public Animation()
        {
            PlayHandler func = () => Console.WriteLine("Jump");
            this.Callback += func;
            this.Callback -= func;
            this.Callback += func;
            this.Callback();
        }

    }
}

更多取消匿名事件的资料参见官方文档:http://msdn.microsoft.com/en-us/library/ms366768.aspx


4.如果不手动注销事件函数,系统会帮我们回收吗?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestCSharp
{
    class EventSource
    {
        public delegate void PlayHandler();
        public event PlayHandler Callback;

        public void Excute()
        {
            if (this.Callback != null)
            {
                this.Callback();
            }
        }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestCSharp
{
    class Listener
    {
        public Listener(EventSource source)
        {
            source.Callback += Run;
        }

        private void Run()
        {
            Console.WriteLine("Run");
        }

        ~Listener()
        {
            Console.WriteLine("Listener 销毁");
        }
    }
}
using System;
using System.Collections;
using System.Collections.Generic;

namespace TestCSharp
{
 
    class MainClass
    {
        public static void Main(string[] args)
        {
            EventSource s = new EventSource();
            Listener l = new Listener(s);
            s.Excute();
            l = null;
            Gc();
            s.Excute();
            s = null;
            Gc();
        }

        public static void Gc()
        {
            Console.WriteLine("GC 开始");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            Console.WriteLine("GC 结束");
        }
    }
   
}

注意第二次调用s.Excute()前,已将listener赋为null,并执行Gc,但是仍然输出Run。直到将source赋为null时,listener才被回收掉。
下面我们手工注销事件,看看系统能不能自动回收?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestCSharp
{
    class Listener
    {
        public Listener(EventSource source)
        {
            source.Callback += Run;
        }

        //添加一个手工注销方法
        public void DeregisterEvent(EventSource source)
        {
            source.Callback -= Run;
        } 

        private void Run()
        {
            Console.WriteLine("Run");
        }

        ~Listener()
        {
            Console.WriteLine("Listener 销毁");
        }
    }
}

using System;
using System.Collections;
using System.Collections.Generic;

namespace TestCSharp
{
 
    class MainClass
    {
        public static void Main(string[] args)
        {
            EventSource s = new EventSource();
            Listener l = new Listener(s);
            s.Excute();
            //手工调用注销
            l.DeregisterEvent(s);
            l = null;
            Gc();
            s.Excute();
            s = null;
            Gc();
        }

        public static void Gc()
        {
            Console.WriteLine("GC 开始");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            Console.WriteLine("GC 结束");
        }
    }
   
}


这次我们可以看到run执行一次,listener也早早的被回收了。

更多系统回收资料参见:http://www.codeproject.com/Articles/29922/Weak-Events-in-C




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值