国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

對C#下函數,委托,事件的一點理解!

2019-11-18 16:50:53
字體:
來源:轉載
供稿:網友

今天一來是有點空,二來是在博客上偶然看到有關于委托的文章,一時興起,就自己也寫一點心得與大家分享一下。

先看一個例子:

using System;
namespace Consoleapplication1
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            bool m_isRight = false;
            object m_obj = m_isRight?MyWrite("true"):MyWrite("false");
            Console.Write(m_obj);
        }
        static PRivate int MyWrite(object i_string)
        {
            Console.Write(i_string);
            return i_string.ToString().Length;
        }
    }
}

問輸出的結果是什么?有一個剛學習程序設計不久的學生的回答是:false false

這個結果給我的映像很深,為什么呢?因為我覺得這個不僅僅是學生的一個錯誤,而更多的是這個學生深入的思考了問題。

因為m_obj是一個對象,所以這個學生理解為:MyWrite()這個函數對象可以直接賦值給m_obj,然后m_obj就當成MyWrite()這個函數來調用,所以他就認為:


Console.Write (m_obj); 等于是:Console.Write (MyWrite(“false”));
這是思維是很有創意的,不是嗎?

于是就是C#里而很多人不好理解的委托了。其實,從使用上講,它就是一個函數變量!如上面的例子,如果真的是想把MyWrite()做為對象賦值給m_obj會是個什么結果呢?

我覺得我們先得解決以下幾個問題,才能正確的把函數當成變量賦值給一個對象:

1、如果可以給一個對象賦函數值,如何來區別不同的函數?

2、如何區別它是一個函數賦值,還是一個普通的對象賦值?

3、如何用這個對象來調用原來的函數?

如果把這幾個問題解決了,委托也就明白了一半。

先看問題1,如果可以給一個對象賦函數值,如何來區別不同的函數?

首先應該明白的是:C#里是可以對一個對象賦函數值的。解決這個問題的辦法是先對該對象申明,申明它可以被什么樣的函數來賦值,而這個對象申明在C#里的學名就是委托。

(在C++里稱為函數指針申明,相應的對象也就叫做函數指針。java里也不同的叫法,可惜我不知道。)

而它的語法就是:

delegate [function declare];

這里的function declare就包括了:

1、函數返回類型,

2、可用來存放函數的對象名(也就是委托名)

3、函數參數

所以完整的定義可以是:

delegate int MyDelegate(object I_object);

當然,合法的委托定義可以是:

delegate void MyDelegate();

delegate void MyDelegate(object I_1,object I_2);

現在,上面的語法就定義了一個抽象的對象MyDelegate, 注意,這里說的是抽象的對象,也就是說,你不能直接給MyDelegate賦函數,而只能在它的實例上函數,這是C#里特殊的要求。它的語法是:

MyDelegate m_delegate = new MyDelegate(與MyDelegate申明一致的函數名);

例如,以下是一個完全的,合法的委托申明與實例化一個對象:

delegate int MyDelegate(object i_object);
//
MyDelegate m_delegate = new MyDelegate(MyWrite);
//MyWrite函數如下,它是滿足委托的申明的。
      static private int MyWrite(object i_string)
      {
  Console.Write(i_string);
  return i_string.ToString().Length;
      }
現在我們就很好的解決了第一個問題,如何定義一個對象,使該對象可以把函數當變量來賦給它。而且,可以區別不同的函數類型,主要是通過函數返回值與函數參數來共區別一類函數。

OK,第二個問題:如果有了這樣的一個對象后,如何來給它賦一個函數值呢?

其實上面的實例化一個委托對象時,就已經給它賦值了。上面的代碼中,m_delegate就已經被賦值MyWrite,因此它已經具有了MyWrite函數的功能。

還有其實它的方法來給它賦值嗎?有,在委托的一個應用中,可以看到其它的賦值方法。也就是另一個不好理解的概念:事件!后面會提到。

我們再來看一下最后一個問題:如何通過一個已經賦值好了的委托對象,還調用它上面賦值了的函數。

這個最簡單了,當一個委托實例賦了函數對象在上面后,就可以像調用原函數一樣的來調用它了。因此,下面是一個會法的調用:基于上面的申明。

m_delegate(“This is a delegate object to call the raw function.”);

它就等同于:

MyWrite(“This is a delegate object to call the raw function.”);

因此,上面的調用與原函數調用一樣,會返回一個int結果。


OK,最后看一個完整的例子:

using System;
namespace ConsoleApplication1
{
    class Class1
    {
        //先申明一個委托對象。
        delegate int MyDelegate(object i_object);
        [STAThread]
        static void Main(string[] args)
        {
            MyDelegate m_delegate = new MyDelegate(MyWrite);
            m_delegate("This is a delegate object to call the raw function.");
        }
        //該函數是滿足上面委托對象的申明的。
        static private int MyWrite(object i_string)
        {
            Console.Write(i_string);
            return i_string.ToString().Length;
        }       
    }
}


再來討論一下它的應用:事件!

事件是其于委托的。我們還是先來看最開始的那個例子:

object m_obj = m_isRight?MyWrite("true"):MyWrite("false");

我想把一個函數對象賦值到m_obj上!但上面的委托只能在實例化對象的時候就直接給它賦值了。而現在是,在運行時對一個委托賦函數值??梢宰龅絾??

同樣是有這樣的向個問題,當然,前提是我們已經知道有一種對象叫委托,它的實例可以賦函數對象。

下面的問題是:

1、如果可以在運行時給某個“特殊委托”賦函數對象,如何實現?

2、運行時,如何知道該“特殊委托”是否已經被賦過函數值?及如何再賦值?

3、如果可以,能否在一個“特殊委托”上添加多個函數?如果可以,如何刪除函數?

下面,我們就針對這幾個問題,來討論一下C#里的事件,也就是上面的“特殊委托”。(其它語言里是如何實現這些功能的,我就不清楚了。)

首先,C#里是可以實現在運行時給一個委托動態的賦函數值的,同時也是可以動態的刪除已經添加在某個委托上的函數的,它的實現有一點點麻煩,就是要用到另一個對象:事件!event

(申明,你完全可以不把它叫事件,只不過這種動態的添加和刪除函數的功能在真實的程序設計中,基本上是與事件相關,所以就叫做事件了。個人想法,呵呵。)

OK,下面是C#語法,來申明一個“特殊委托”――事件,讓它可以動態的添加函數!

下文中,事件是指那些“特殊委托”,它的特殊之外,后面會講到。而下文中的委托就是前面講到的,一個特殊的對象,該對象可以把函數當“值”賦給它。

static event MyDelegate m_myevent;

(static 可以用其它的修飾符)

說明一下,這里其實就是申明了一個事件,用event來說明它是事件(特殊委托)的。其實對比實例化一個委托的語法,你可以理解到,它就像是申明了一個委托,只不過個委托加了個event來說明它:

Mydelegate m_delegate;//申明一個委托

event MyDelegate m_myevent;//申明一個事件

很像吧!不是嗎?

OK,我們再來看,m_myevent 與m_delegate到底有什么不同的?也就是,事件(特殊委托)到底特殊在什么地方?

1、事件不是一個可以直接把函數當值一樣賦給它的委托。而委托可以直接賦函數,而且是在實例化的時候,賦函數名。

2、事件只能把一個實例的委托當值賦給它。也就是說:事件是用來管理委托的,進而來管理函數!因為一個實例化的委托一定有一個函數與之對應。

3、在事件上可以動態的添加與刪除委托。而委托上不能動態的添加刪除函數。

OK,下面的一個問題,上面事件的申明中,MyDelegate是起什么作用的呢?

還記得前面的委托申明嗎?它就是說明了m_myevent在運行時可以動態的以委托的形式賦的函數要與MyDelegate申明的一樣!

因此上面的一個實例化是完全合法的。

再理解一下:事件,是用來動態管理委托的,而委托是單一的與一個函數對應的。

 

現在看第二個問題,運行時,如何知道該“特殊委托”是否已經被賦過函數值?及如何再賦值?

即:如何知道一個事件上已經賦過經過委托過的函數?

前面已經說過,m_myevent沒有給它賦值,如何給它賦值呢?它的賦值方法有點怪:

一個實例:

m_myevent += m_delegate;

有點怪吧!這里正好說明了:事件是用來動態管理委托的。把一個委托加在事件上。

當然,你還可以在添加委托的時候直接new一個新的委托:

m_myevent +=new MyDelegate(Class1_m_myevent);//后面的函數名由vs2003生動生成

這就是.net下標準的給事件添加委托的方法,也就是給一個事件添加了一個可調用的函數。確切的說,是一個回調函數(C++的概念)。

 

OK,下面就如何判斷一個事件上是否已經被賦過經過委托的函數:

if(m_myevent==null)

就可以知道了!

那么如何知道一個事件上面有多少個委托呢?也就是多少個委托過的函數?

m_ myevent.GetInvocationList();

可以得到所有的委托!這里應該知道:事件本身是一個對象,當實例化一個事件后,它是有自己的一些方法也成員的??梢圆殚哅SDN得到更多說明,同樣的委托也是一個對象,相關的說明也可以在MSDN里找到。

最后的問題:如何刪除一個已經添加在事件上的委托?

太容易而且太有意思了:

m_myevent -= m_delegate;

那么這樣的幾個問題又來了:

1.如果這個事件上沒有該委托,“減掉”以后會出錯嗎?不會,放心的減吧。

2.如何調用這個事件上的委托呢?上面有多個委托,它是怎樣運行呢?

調用事件上的委托與調用委托上的函數是完全一樣的:你要給出與委托申明一樣的函數參數,并且調用會返回與申明一樣的數據類型。

最后再回來看這個問題:

object m_obj = m_isRight?MyWrite("true"):MyWrite("false");

如何解決它呢?把一個函數賦給一個對象:一個例示(僅做演示解決這個問題,個人認為這樣的做法沒有實用意義)

 


using System;
namespace ConsoleApplication1
{
    public class Class4
    {
        event System.EventHandler m_obj;
        public Class4()
        {
            System.EventHandler m_f1 = new EventHandler(SomeFunc1);
            System.EventHandler m_f2 = new EventHandler(SomeFunc2);
            bool m_someCondition = false;
            m_obj += m_someCondition?m_f1:m_f1;
            m_obj(this,null);
        }

        private void SomeFunc1(object sender, EventArgs args)
        {
        }

        private void SomeFunc2(object sender, EventArgs args)
        {
        }
    }
}


最后看一個完整的例子:


using System;
namespace ConsoleApplication1
{
    class Class1
    {
        //先申明一個委托對象。
        delegate int MyDelegate(object i_object);
        static event MyDelegate m_myevent;
        [STAThread]
        static void Main(string[] args)
        {
            MyDelegate m_delegate = new MyDelegate(MyWrite);
            m_delegate("This is a delegate object to call the raw function.");
            m_myevent += m_delegate;
            m_myevent += new MyDelegate(MyWrite);
            m_myevent +=new MyDelegate(Class1_m_myevent);
            if(m_myevent!=null)
            {
   m_myevent("This is a event to call the funcaion on the delegate.");
            }           
        }
        //該函數是滿足上面委托對象的申明的。
        static private int MyWrite(object i_string)
        {
            Console.WriteLine(i_string);
            return i_string.ToString().Length;
        }

        private static int Class1_m_myevent(object i_object)
        {
            Console.WriteLine(i_object);
            return 0;
        }
    }
}

 

 

我們再來看一個.net下標準的事件驅動模型的例子:


using System;

namespace ConsoleApplication1
{
    public delegate void MyEventHandle(object i_sender,object i_arg);

    public class Class2
    {
        public Class2()
        {
        }

        [STAThread]
        static void Main2(string[] args)
        {
            Class3 m_runOject = new Class3();
            m_runOject.OnError += new MyEventHandle(m_runOject_OnError);
            m_runOject.OnSomeThingHappened += new MyEventHandle(m_runOject_OnSomeThingHappened);
            m_runOject.Run();
        }

        private static void m_runOject_OnError(object i_sender, object i_arg)
        {
            Console.WriteLine("Error in {0}, arg:{1}",i_sender,i_arg);
            Console.WriteLine("Object {0} will stop running.",i_sender);
            (i_sender as Class3).Stop();
        }

        private static void m_runOject_OnSomeThingHappened(object i_sender, object i_arg)
        {
            Console.WriteLine("Something happended in {0}, arg:{1}",i_sender,i_arg);
        }
    }


    public class Class3
    {
        public bool m_isStop = false;
        public event MyEventHandle OnSomeThingHappened;
        public event MyEventHandle OnError;

        public Class3()
        {
        }

        public void Run()
        {
            Random m_rand = new Random();
            int m_randomNum = m_rand.Next();
            while(!m_isStop)
            {
   if(m_isStop){break;}
   m_randomNum = m_rand.Next(100);
   if(m_randomNum%5==0)
   {
       if(this.OnError!=null)
       {
           this.OnError(this,m_randomNum);
       }
   }
   else
   {
       if(this.OnSomeThingHappened!=null)
       {
           this.OnSomeThingHappened(this,m_randomNum);
       }
   }
            }
        }

        public void Stop()
        {
            m_isStop = true;
        }
    }
}


好了,全部完了!

 

最后再從另一個角度來理解:函數,委托和事件!

1、函數,是程序的基本單元,在.net下,有一個很重要的思想,就是:一切對象化!你可把一個int boxing后得到一個object,也可以把一個object unboxing后得到一個int. 可惜,就是沒有函數對象化這個概念???

2、其實函數也可以對象化!就是通過delegate,委托就是把函數作為對象來處理,使它函數也具有了對象的一些特點。在實例化一個委托的時候,就是把一個函數”boxing”。因此,委托本質上就是一個類!它的初始化參數是一個函數名!不同的委托是不同的類,對應不同類型的函數。

3、事件是對委托的一個管理封裝,它可以很好的動態管理委托,從而完成很多有實用價值的事情,最主要的就是事件!

完全是個人理解,有不同意見,歡迎討論!


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 思茅市| 弋阳县| 辽阳县| 宁河县| 邹城市| 咸阳市| 镇远县| 临泉县| 阆中市| 甘德县| 图木舒克市| 泌阳县| 偃师市| 宕昌县| 永嘉县| 博湖县| 宜春市| 临汾市| 财经| 迭部县| 寿光市| 松原市| 中江县| 方城县| 安康市| 洮南市| 盱眙县| 石门县| 武穴市| 石门县| 唐河县| 光泽县| 望奎县| 镇赉县| 紫阳县| 建平县| 汶上县| 罗江县| 金阳县| 阿鲁科尔沁旗| 大宁县|