查看: 1331|回复: 4
打印 上一主题 下一主题

[经验分享] 关于Delegate的方方面面

[复制链接]

100

主题

3

听众

7683

积分

高级设计师

Rank: 6Rank: 6

纳金币
2378
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

跳转到指定楼层
楼主
发表于 2015-2-1 23:44:25 |只看该作者 |倒序浏览
我是否需要阅读这篇文章

Code1:[C#]
private delegate void BloodNumDelegate ();
public delegate void ExpNumChangeDelegate (int _currentExpNum);
public event ExpNumChangeDelegate mOnExpNumChangeDelegate;


Code2:[C#]
PlayerVoProxy.instance.mOnExpNumChangeDelegate += delegate(int _expNum)
{
    Console.WriteLine ("阿呆收到, ExpNum is : {0}", _expNum);
};


如果你可以很清楚明白上面两部分代码是什么意思,说明你已经很了解Delegate(至少比我了解 ),
所以我想下面的文章也许对你的帮助不是很大. 不过如果某些地方你也有所疑惑,希望你能从我下面的文章中得到一些帮助.
这篇文章都包含哪些内容
这篇文章从最基本的Delegate开始介绍起,后续会引申到event, 匿名Delegate 及我个人对其使用的一点理解
提前需要阅读的文章
如果你像我当初一样对Delegate完全不理解,请先阅读NET之美:.NET关键技术深入解析 迷你书 中的 第3章 (C# 中的委托和事件).  
(作者对Delegate理解的及其深入,我是比不上了 )
什么是 Delegate
从观察者模式说起
观察者模式大家肯定都不陌生
[C#]
using System;
using System.Collections;
namespace L1
{
    public class FakeBloodChangeDelegate
    {
        public ArrayList mAllFakeDelegateListener;
        public FakeBloodChangeDelegate ()
        {
            mAllFakeDelegateListener = new ArrayList ();
        }
        public void addListener (IFakeBloodNumChangeDelegateListener _listener)
        {
            mAllFakeDelegateListener.Add (_listener);
        }
        public void upldateListener ()
        {
            foreach (IFakeBloodNumChangeDelegateListener listener in mAllFakeDelegateListener)
            {
                listener.onBloodNumChange ();
            }
        }
    }
}
[C#]
using System;
namespace L1
{
    public interface IFakeBloodNumChangeDelegateListener
    {
        void onBloodNumChange ();
    }
}
[C#]
using System;
namespace L1
{
    public class Idiot : IFakeBloodNumChangeDelegateListener
    {
        public Idiot ()
        {
        }

        public void onBloodNumChange ()
        {
            Console.WriteLine ("Idiot --> onBloodNumChange");
        }
    }
}




[C#]
using System;
namespace L1
{
    public class TestArea1
    {
        private FakeBloodChangeDelegate mFakeBloodChangeDelegate;
        private Idiot mIdiot;

        public TestArea1 ()
        {
        }

        public void run ()
        {
            mFakeBloodChangeDelegate = new FakeBloodChangeDelegate ();
            mIdiot = new Idiot ();

            mFakeBloodChangeDelegate.addListener (mIdiot);

            mFakeBloodChangeDelegate.upldateListener ();
        }
    }
}



这是一个比较标准的观察者, idiot是监听的人,当主体(FakeBloodChangeDelegate)发生变化时候调用 updateListener方法告诉所有监听对象
Ok, 接下来让我们稍微对这个FakeBloodChangeDelegate类进行一个简单的封装, 为其创建一个Player对象,Blood是Player的一部分
[C#]
using System;
namespace L1
{
    public class FakePlayerVoProxy
    {
        public FakeBloodChangeDelegate bloodNumChangeListner;

        private int mCurrentBloodNum = 0;

        public FakePlayerVoProxy ()
        {
            bloodNumChangeListner = new FakeBloodChangeDelegate ();
        }

        public void addPlayerBlood (int _addNum)
        {
            mCurrentBloodNum += _addNum;
            bloodNumChangeListner.upldateListener ();
        }

    }
}

于是,主类中的调用及变为:
[C#]
using System;
namespace L1
{
    public class TestArea1
    {
        private FakePlayerVoProxy mPlayerVo;
        private Idiot mIdiot;

        public TestArea1 ()
        {
        }

        public void run ()
        {
            mPlayerVo = new FakePlayerVoProxy ();
            mIdiot = new Idiot ();

            mPlayerVo.bloodNumChangeListner.addListener (mIdiot);
            mPlayerVo.addPlayerBlood (10);
        }
    }
}


运行一下代码,会得到和上面一样的输出。 毕竟其实只是简单的对FakeBloodChangeDelegate进行一个封装,没有改变任何的代码。
如果上面的例子可以完全理解,就让我们往下继续

Delegate 就是一个类这个其实是一个很重要的信息,在上面提到的那本迷你书里面已经说的非常清楚了.
我之所以把代码蹩脚的写成这个模样 其实也是为了说明这件事。

上面例子中的FakeBloodChangeDelegate,包括对应的Listener可以简化为一行代码
public delegate void BloodNumChangeDelegate ();
先抛开public不说, 后面用 delegate void BloodNumChangeDelegate(); 相当于就是告诉编辑器为你创建出 一个FakeBloodChangeDelegate 和 IFakeBloodNumChangeDelegateListener
(其实这块是不准确的表达,不过目前可以先不要细纠结到时是怎么回事,当看完后面的内容后 可以回头再去看 上面我提到的那本迷你书,里面讲的很详细)
[C#]
using System;
namespace L1
{
    public class FakePlayerVoProxy
    {
        public delegate void BloodNumChangeDelegate ();

        public BloodNumChangeDelegate bloodNumChangeListner;

        private int mCurrentBloodNum = 0;

        public FakePlayerVoProxy ()
        {
        }

        public void addPlayerBlood (int _addNum)
        {
            mCurrentBloodNum += _addNum;
            bloodNumChangeListner ();
        }
    }
}


注意
public delegate void BloodNumChangeDelegate (); 这行表示创建了FakeBloodChangeDelegate 类其实和 FackPlayerVoProxy这里类没有任何关系,
完全可以单独建立一个文件,起名Apple,然后把这行放在那个Apple的类里面 不过根据这个类(Delegate)创建对象时候你就不能写

public BloodNumChangeDelegate bloodNumChangeListner;
而要写
public Apple.BloodNumChangeDelegate bloodNumChangeListner;



个人关于Delegate的一点理解

因为是写AS出身(ActionSprite),所以对于回调函数一点都不陌生,而Delegate就可以理解为C#中的回调函数,唯一会比较费解的会是

public delegate void BloodNumChangeDelegate ();
public BloodNumChangeDelegate bloodNumChangeListner;
这两行代码,这块的核心就是要理解 Delegate其实就是告诉编译器为你生成一个实现了观察者模式的类.





关于Delegate前面的Public修饰符

之前我在解释Delegate是怎么一回事时候,故意忽略掉了其前面的类修饰符 public. 这块其实还是很有文章的. 对于Delegate或者回归本质,对于观察者模式,最主要的三个函数应该是

addListener,  removeListener,  updateListener

而OO的思想就是 一定要封装 对于外部监听者 只应该能访问到 addListener, removeListener   而upldateListner 这个应该只有主类才可以做的事情.

让我们先退回到实现Delegate之前的例子上面,继续使用FakeBloodChangeDelegate ,
若要实现以上的需求,仅需要对应的将FakeBloodChangeDelegate改为private 并提供相应的public函数即可
[C#]
using System;
namespace L1
{
    public class FakePlayerVoProxy
    {
        private FakeBloodChangeDelegate mBloodNumChangeListner;

        private int mCurrentBloodNum = 0;

        public FakePlayerVoProxy ()
        {
            mBloodNumChangeListner = new FakeBloodChangeDelegate ();
        }

        public void addListener(IFakeBloodNumChangeDelegateListener _listener)
        {
            mBloodNumChangeListner.Add(_listener);
        }

        public void removeListener(IFakeBloodNumChangeDelegateListener _listener){//对应Remove代码}

                 public void addPlayerBlood (int _addNum)
        {
            mCurrentBloodNum += _addNum;
            bloodNumChangeListner.upldateListener ();
        }

    }
}

这块应该很好理解吧?  所以呢如果使用Delegate去实现同样的操作及可以写成:
[C#]

using System;
namespace L1
{
    public class FakePlayerVoProxy
    {
        public delegate void BloodNumChangeDelegate ();

        private BloodNumChangeDelegate bloodNumChangeListner;

        private int mCurrentBloodNum = 0;

        public FakePlayerVoProxy ()
        {
        }

        public void addListener (?? _listener)
        {
            bloodNumChangeListner += _listener;
        }
            

        public void addPlayerBlood (int _addNum)
        {
            mCurrentBloodNum += _addNum;
            bloodNumChangeListner ();
        }
    }
}



你会发现 在addListener这里 不知道该怎么写了(其实是可以写的,不过如果这块你都理解怎么写了也就不用再继续往下读啦 哈),  这个 _listener应该是什么类型呢?




使用Event关键字

在推荐的那本迷你书里面(什么?! 还没看...) 已经把这块说的非常明白了, 为了封装Delegate,对外仅提供add,remove 但是不提供
update方法, 但是如果不写成public 又没办法对外访问,所以及产生了 Event这个关键字
[C#]

using System;
namespace L1
{
    public class FakePlayerVoProxy
    {
        public delegate void BloodNumChangeDelegate ();

        public event BloodNumChangeDelegate bloodNumChangeListner;

        private int mCurrentBloodNum = 0;

        public FakePlayerVoProxy ()
        {
        }

        public void addPlayerBlood (int _addNum)
        {
            mCurrentBloodNum += _addNum;
            bloodNumChangeListner ();
        }
    }
}


[C#]
using System;
namespace L1
{
    public class TestArea1
    {
        private FakePlayerVoProxy mPlayerVo;
        private Idiot mIdiot;

        public TestArea1 ()
        {
        }

        public void run ()
        {
            mPlayerVo = new FakePlayerVoProxy ();
            mIdiot = new Idiot ();

            mPlayerVo.bloodNumChangeListner += mIdiot.onBloodNumChange;

            mPlayerVo.addPlayerBlood (10);
        }
    }
}


如果在TestArea1中调用 mPlayerVo.bloodNumChangeListner ();
会收到
Error(14,14): Error CS0070: The event `L1.FakePlayerVoProxy.bloodNumChangeListner' can only appear on the left hand side of += or -= when used outside of the type `L1.FakePlayerVoProxy' (CS0070) (L1)

也就是说在类的外部是无法调用"updateListener" 这个方法了. 这样也就符合了OO得封装原则




结束语

到此, Delegate和Event的用法 我想大家应该有了一个初步的认识,后续只要在项目中多试几次 应该就能有很好的掌握吧
(其实我接触C#也才不到一周,所以能理解的也就到这里了,希望对大家有帮助.)



分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

711

主题

10

听众

5805

积分

高级设计师

Rank: 6Rank: 6

纳金币
2954
精华
3

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

沙发
发表于 2015-2-1 23:49:00 |只看该作者
里面的文字为啥靠右边了啊
回复

使用道具 举报

100

主题

3

听众

7683

积分

高级设计师

Rank: 6Rank: 6

纳金币
2378
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

板凳
发表于 2015-2-1 23:57:09 |只看该作者
刀锋狼 发表于 2015-2-1 23:49
里面的文字为啥靠右边了啊

看到排版有问题,刚才再编辑啊。。
回复

使用道具 举报

20

主题

5

听众

1090

积分

助理设计师

Rank: 4

纳金币
105
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

地板
发表于 2015-2-5 11:47:10 |只看该作者

Thanks for sharing this one !
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|纳金网 ( 闽ICP备08008928号

GMT+8, 2024-4-29 11:08 , Processed in 0.140733 second(s), 27 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部