程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 刨根問底U3D---關於Delegate的方方面面,u3d---delegate

刨根問底U3D---關於Delegate的方方面面,u3d---delegate

編輯:C#入門知識

刨根問底U3D---關於Delegate的方方面面,u3d---delegate


 

我是否需要閱讀這篇文章

Code1:

1 private delegate void BloodNumDelegate ();
2 
3 public delegate void ExpNumChangeDelegate (int _currentExpNum);
4 
5 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;

        private int mCurrentBloodNum = 0;

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

運行一下代碼,會得到和上面一樣的輸出。 畢竟其實只是簡單的對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;

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

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去實現同樣的操作及可以寫成:

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這個關鍵字

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

    }
}

 

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#也才不到一周,所以能理解的也就到這裡了,希望對大家有幫助.)

 


等等! 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;

        private int mCurrentBloodNum = 0;

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

    }
}

以上兩段代碼是可以編譯過並且可以運行的,  這個是我突然看到匿名委托使用的是Delegate修飾符所想到的,不過我對這塊理解的也不是很透  如果有人知道原因 麻煩也給我解釋一下吧

相關的內容 也許和 溫故而知新:Delegate,Action,Func,匿名方法,匿名委托,事件 這篇文章提到的內容有關.


That's All

Eran.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved