我是否需要阅读这篇文章

Code1:

 private delegate void BloodNumDelegate ();

 public delegate void ExpNumChangeDelegate (int _currentExpNum);

 public event ExpNumChangeDelegate mOnExpNumChangeDelegate;

Code2:

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

如果你可以很清楚明白上面两部分代码是什么意思,说明你已经很了解Delegate(至少比我了解:) ),

所以我想下面的文章也许对你的帮助不是很大. 不过如果某些地方你也有所疑惑,希望你能从我下面的文章中得到一些帮助.


这篇文章都包含哪些内容

这篇文章从最基本的Delegate开始介绍起,后续会引申到event, 匿名Delegate 及我个人对其使用的一点理解

提前需要阅读的文章

如果你像我当初一样对Delegate完全不理解,请先阅读

NET之美:.NET关键技术深入解析.NET之美:.NET关键技术深入解析 迷你书 中的 第3章 (C# 中的委托和事件).  

(作者对Delegate理解的及其深入,我是比不上了 :( )


什么是 Delegate

从观察者模式说起

观察者模式大家肯定都不陌生

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 ();
            }
        }
    }
}

using System;

namespace L1
{
    public interface IFakeBloodNumChangeDelegateListener
    {
        void onBloodNumChange ();
    }
}

using System;

namespace L1
{
    public class Idiot : IFakeBloodNumChangeDelegateListener
    {
        public Idiot ()
        {
        }

        public void onBloodNumChange ()
        {
            Console.WriteLine ("Idiot --> onBloodNumChange");
        }
    }
}
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的一部分

using System;

namespace L1
{
    public class FakePlayerVoProxy
    {
        public FakeBloodChangeDelegate bloodNumChangeListner;

        ;

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

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

    }
}

于是,主类中的调用及变为:

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 ();
        }
    }
}

运行一下代码,会得到和上面一样的输出。 毕竟其实只是简单的对FakeBloodChangeDelegate进行一个封装,没有改变任何的代码。

如果上面的例子可以完全理解,就让我们往下继续

Delegate 就是一个类

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

上面例子中的FakeBloodChangeDelegate,包括对应的Listener可以简化为一行代码

public delegate void BloodNumChangeDelegate ();

先抛开public不说, 后面用 delegate void BloodNumChangeDelegate(); 相当于就是告诉编辑器为你创建出 一个

FakeBloodChangeDelegate 和 IFakeBloodNumChangeDelegateListener (其实这块是不准确的表达,不过目前可以先不要细纠结到时是怎么回事,当看完后面的内容后 可以回头再去看 上面我提到的那本迷你书,里面讲的很详细)

using System;

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

        public BloodNumChangeDelegate bloodNumChangeListner;

        ;

        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函数即可

using System;

namespace L1
{
    public class FakePlayerVoProxy
    {
        private FakeBloodChangeDelegate mBloodNumChangeListner;

        ;

        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去实现同样的操作及可以写成:

using System;

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

        private BloodNumChangeDelegate bloodNumChangeListner;

        ;

        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这个关键字

using System;

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

        public event BloodNumChangeDelegate bloodNumChangeListner;

        ;

        public FakePlayerVoProxy ()
        {
        }

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

    }
}
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 ();
        }

    }
}

如果在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#也才不到一周,所以能理解的也就到这里了,希望对大家有帮助.)


等等! Code2中的 代码是什么?

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

其实这个代码 可以理解为匿名函数( 其实是匿名委托)

比如对于一个按钮

private void fun1()
{
   dummyBtn.onClick+=fun2
}

private void fun2()
{
   //Fun2Code
}

但是如果fun2的代码很简单 或者其他原因 也可以使用 匿名委托 也就是如上的写法

private void fun1()
{
   dummyBtn.onClick+=delegate()
   {
       //Fun2Code
   }
}

一般在按钮的处理以及Tween操作时候大多愿意这样去做,不过我个人还是不太爱使用匿名委托(As3中是匿名函数)这种方式做处理. 因人而异吧.


真的不能使用 private BloodNumChangeDelegate bloodNumChangeListner 么?

using System;

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

        private BloodNumChangeDelegate bloodNumChangeListner;

        ;

        public FakePlayerVoProxy ()
        {
        }

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

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

    }
}
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.addListener (mIdiot.onBloodNumChange);

            mPlayerVo.addPlayerBlood ();
        }

    }
}

以上两段代码是可以编译过并且可以运行的,  这个是我突然看到匿名委托使用的是Delegate修饰符所想到的,不过我对这块理解的也不是很透  如果有人知道原因 麻烦也给我解释一下吧

相关的内容 也许和 温故而知新:Delegate,Action,Func,匿名方法,匿名委托,事件 这篇文章提到的内容有关.

That's All

Eran.

刨根问底U3D---关于Delegate的方方面面的更多相关文章

  1. Cpu Gpu 内存 显存 数据流

    [精]从CPU架构和技术的演变看GPU未来发展 http://www.pcpop.com/doc/0/521/521832_all.shtml 显存与纹理内存详解 http://blog.csdn.n ...

  2. iOS delegate

    有两个scene,分别为Scene A和Scene B.Scene A上有一个UIButton(Button A)和一个UILable(Lable A):Scene B上有一个UITextFiled( ...

  3. 刨根问底U3D---如何退出Play模式后保留数据更改

    实际中遇到的需求 在做一款对抗类游戏,目前正在调整游戏的平衡性 所以就产生了一个需求 希望可以在Play模式时候对数据源做的更改可以在退出时候被保存下来. 举个Case, 比如 有一个炮塔 可以发射子 ...

  4. U3D C# 实现AS3事件机制

    写了很多年的AS3,最近接触U3D感觉事件机制没AS3的爽.咬紧牙关一鼓作气 基于C# 的委托实现了一版.废话不多说上干货. EventDispatcher代码如下: using UnityEngin ...

  5. ios原生项目内嵌u3d工程

    本文一反常态,目标是把u3d工程以framewWork形式 内嵌原生IOS项目 1.xcode中新建Cocoa Touch FrameWork.取名u3dFrameWork 2.把u3d导出的xcod ...

  6. 总结的U3D面试题

    1.配置Unity 3d调试环境? 1)          Visual Studio Tools for Unity 2)          访问http://unityvs.com 3)      ...

  7. U3D面试五

    U3D面试题 配置Unity3D调试环境 Visual Studio Tools for Unity 访问http://unityvs.com 安装对应的版本 使用方法(生成项目文件,如何调试) Ar ...

  8. U3D面试题四

    1.配置Unity3D调试环境 在windows环境下,设置unity3d的编辑器调试环境方法: 点击“Edit‘---”Preferences“,弹出如下窗口 选择MonoDeveop即可. 在编辑 ...

  9. Delegate,Action,Func,匿名方法,匿名委托,事件

    一.委托Delegate 一般的方法(Method)中,我们的参数总是string,int,DateTime...这些基本的数据类型(或者没有参数),比如 public void HelloWorld ...

随机推荐

  1. cursor:pointer

    .cursor_hand{ cursor:pointer; }

  2. Transform.InverseTransformPoint 反向变换点

    JavaScript ⇒ public function InverseTransformPoint(position: Vector3): Vector3; C# ⇒public Vector3 I ...

  3. WAMP数据库环境搭建

    php.ini: date.timezone = Etc/GMT-8//设置北京时间 my.ini: character_set_server=utf8//设置utf8 innodb_force_re ...

  4. 在XE5中 VCL空窗体的3个线程

    中午看到技术群里有人讨论, XE5一个空窗体程序就包含了3个线程, 赶忙打开XE5开了个空窗体一看, 果然如此 再打开D7和2010看了一下, 都是一个线程 这时看到有人说一个是输入法, 一个是GDI ...

  5. Bluetooth RFCOMM介绍

    目录 1. 介绍 2. 服务概述 2.1 RS-232控制信号 2.2 Null Modem Emulation 2.3 多串口仿真 3. 服务接口描述 4. RFCOMM帧类型 5. RFCOMM帧 ...

  6. Qt持久性对象进行序列化

    Mfc和Java中自定义类的对象都可以对其进行持久性保存,Qt持久性对象进行序列化当然也是必不可少的.不过这个问题还真困扰了我很长时间……Mfc通过重写虚函数Serialize().Java则是所属的 ...

  7. Windows下一个比较完美的线程池实现(使用线程池实现的Http上传下载实现)

    http://blog.csdn.net/fishjam/article/details/8632049 http://download.csdn.net/user/fishjam

  8. Ajax如何实现跨域问题

    一个域名的组成 http:// www . abc.com : 8080 /scripts/jquery.js 协议 子域名 主域名 端口号 请求资源地址 当协议.子域名.主域名.端口号中任意一个不同 ...

  9. [LeetCode]题解(python):089 Gray Code

    题目来源 https://leetcode.com/problems/gray-code/ The gray code is a binary numeral system where two suc ...

  10. 二级c程序设计题(2)

    原文:http://www.cnblogs.com/imaker/p/6128049.html 所属年份:2010.9;2012.3编写函数fun,其功能是:根据以下公式求π的值(要求精度0.0005 ...