我是否需要阅读这篇文章

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. mysql安装tcmalloc

    TCMalloc(Thread-Caching Malloc)是google-perftools工具中的一个,与标准的glibc库的malloc相比,TCMalloc在内存的分配上效率和速度要高得多, ...

  2. Python - 求斐波那契数列前N项之和

    n = int(input("Input N: ")) a = 0 b = 1 sum = 0 for i in range(n): sum += a a, b = b, a + ...

  3. php 分词 —— PHPAnalysis无组件分词系统

    分词,顾名思义就是把词语分开,从哪里分开?当然是一大堆词语里了,一大堆词语是什么?是废话或者名言.这在数据库搜索时非常有用. 官方网站 http://www.phpbone.com/phpanalys ...

  4. 【转】C# 解析JSON格式数据

    http://blog.csdn.net/coolszy/article/details/8606803 JSON简介 JSON(全称为JavaScript ObjectNotation) 是一种轻量 ...

  5. Bluetooth HFP介绍

    目录 1. 介绍 1.1 目的 1.2 使用场景 1.3 依赖关系 1.4 协议栈 1.5 角色 2. 应用层 3. 空白章节 4. 互操作性要求 4.1 介绍 4.2 Service Level C ...

  6. nrf51822裸机教程-PPI

    Programmable Peripheral Interconnect即可编程外设互联 系统,该模块是51822 提供的一个特性. 目的是为了让51822 的外围模块可以不通过处理器而自动相互作用. ...

  7. java JDK8 学习笔记——第17章 反射与类加载器

    第十七章 反射与类加载器 17.1 运用反射 反射:.class文档反映了类基本信息,从Class等API取得类信息的方式称为反射. 17.1.1 Class与.class文档 1.java.lang ...

  8. int a=5,则 ++(a++)的值是?

    编译出错:++(a++)先计算的是括号里的(a++),返回的结果是一个表达式,其值是5,不能对表达式进行赋值

  9. oracle中截取某个字符前面和后面的值

    创建测试表及数据 create table test(name varchar2(10)); insert into test values ('2-15');insert into test val ...

  10. 再谈CocoaPods

    1. 简介 java语言的第三方库管理工具是Maven,Node.js的第三方库管理工具是npm,而ios的第三方库管理工具是CocoaPods. CocoaPods 的原理是将所有的依赖库都放到名为 ...