按本身的想法去理解事件和泛型(C#)

上一篇那些年困擾咱們的委託(C#)講了委託,這一篇天然就輪到事件了。html

不喜歡官方的表達方式,喜歡按照本身的想法去理解一些抽象的東西,我是一個喜歡簡單怕麻煩的人。windows

事件

考慮到委託使用的一些缺陷,就有了事件。委託是不安全的,打個比方,若是把委託看成共有字段,那麼事件就至關因而屬性的概念。安全

事件就是被限制使用的委託變量,事件裏面封裝了一個多播委託。app

事件語法:public event 委託類型 事件名;ide

事件的做用:事件的做用與委託變量同樣,只是功能上比委託變量有更多的限制。好比:只能經過+=或者-=來綁定方法。只能在類內部調用事件。工具

當一個結果發生時,有可能引發另外的一些反應,這就好像因果關係。而事件則是這個因與果的內部聯繫。post

事件的本質:委託的一個實例,添加了event關鍵字修飾。性能

委託是一種類型,事件是委託類型的實例。this

和委託的區別:spa

  • 事件不能用=來註冊方法。(防止外面直接賦值爲null,致使註冊失效)
  • 事件不能被外部調用(安全性控制)

整個windows系統都是經過事件驅動的,事件都有觸發條件。

在WebForm或者WinForm中,咱們常常看到:

        private void button1_Click(object sender, EventArgs e)
        {
            //代碼
        }

上面是一個按鈕的單擊事件。從上能夠看到三個事件因素:

  • 對象:button
  • 事件名:click
  • 參數:object sender,事件源,在這裏其實就是button,eventArgs e是事件須要的資源數據。

咱們在Winform中都是經過以下的方式來註冊事件的。

this.button1.Click += new System.EventHandler(this.button1_Click);

EventHandler就是一個委託:

public delegate void EventHandler(object sender, EventArgs e);

這也就是爲何咱們註冊的事件老是有sender和e這兩個參數,由於委託就是這樣聲明的。咱們來自定義一個事件:

        public event EventHandler OnSay;
        public Form1()
        {
            InitializeComponent();
            OnSay += Form1_OnSay;
        }

        void Form1_OnSay(object sender, EventArgs e)
        {
            Console.Write("你好嗎");
        }

咱們經過Reflector工具來查看:

事件OnSay中,實際上是2個方法,咱們來看下源碼:

        public void add_OnSay(EventHandler value)
        {
            EventHandler handler2;
            EventHandler onSay = this.OnSay;
            do
            {
                handler2 = onSay;
                EventHandler handler3 = (EventHandler)Delegate.Combine(handler2, value);
                onSay = Interlocked.CompareExchange<EventHandler>(ref this.OnSay, handler3, handler2);
            }
            while (onSay != handler2);
        }
        public void remove_OnSay(EventHandler value)
        {
            EventHandler handler2;
            EventHandler onSay = this.OnSay;
            do
            {
                handler2 = onSay;
                EventHandler handler3 = (EventHandler)Delegate.Remove(handler2, value);
                onSay = Interlocked.CompareExchange<EventHandler>(ref this.OnSay, handler3, handler2);
            }
            while (onSay != handler2);
        }

 這裏能夠看出對事件的操做,其實最終仍是體如今對委託的操做。

泛型

爲何要有泛型?

更好的實現代碼複用,可是它不是經過面向對象的思想來實現代碼複用。面向對象慣用的三板斧:封裝、繼承、多態。

咱們先來看一下代碼,假設在一個類中有多個方法,他們的操做很相似,可能僅僅只是傳入的參數類型不一樣而已

using System;

namespace GenericsDemo
{
    public class MethodTest
    {
        public void IntShow(int i)
        {
            Console.WriteLine(string.Format("IntShow方法,參數類型{0}",i.GetType()));
        }
        public void StrShow(string s)
        {
            Console.WriteLine(string.Format("StrShow方法,參數類型{0}", s.GetType()));
        }
    }
}

若是一個類中存在多個這樣的方法,咱們總不能把全部的方法都這麼寫一遍吧,有沒有一種方式來將這些方法進行合併呢?

這個時候咱們會想到Object是任何類型的父類,任何父類出現的地方,均可以使用子類來代替。接下來,咱們來改造一下代碼實現:

        public void ObjShow(object obj)
        {
            Console.WriteLine(string.Format("ObjShow方法,參數類型{0}", obj.GetType()));
        }

咱們來看下調用:

            _MethodTest.IntShow(1);
            _MethodTest.StrShow("1");

            _MethodTest.ObjShow(1);
            _MethodTest.ObjShow("1");

方法是合併了,可是如今存在什麼樣的問題?出現了裝箱拆箱,嚴重影響性能。並且不夠安全,由於若是當我把代碼進行以下修改時,會發生什麼

        public void ObjShow(object obj)
        {
            //Console.WriteLine(string.Format("ObjShow方法,參數類型{0}", obj.GetType()));
            Console.WriteLine(string.Format("ObjShow方法,參數類型{0},參數值{1}", obj.GetType(),Convert.ToInt32(obj)));
        }

調用代碼: _MethodTest.ObjShow("a");

編譯時不會報錯,可是運行時就報錯了。也就是說經過object來做爲參數傳遞,實際上是存在嚴重的安全隱患的。

那麼有沒有什麼辦法來解決這兩個問題呢?C#2.0泛型的出現正是基於這樣的需求。

        public void GenericsShow<T>(T t)
        {
            Console.WriteLine(string.Format("GenericsShow方法,參數類型{0}", t.GetType()));
        }

調用代碼:_MethodTest.GenericsShow<int>(1);

這樣依賴,泛型方法在申明的時候可以實現相似於Objet的效果,在調用時先肯定類型,這樣就達到了安全檢查的目的。

泛型就像是使用了一個類型佔位符,而這一特性在使用集合時更能體現其強大之處。

也正是因爲泛型太強大了,強大得像孫悟空同樣,咱們須要弄一道緊箍咒來對其進行束縛,不然不容易控制。這時,就有了泛型約束,它在泛型方法或者泛型委託聲明之時就對其進行限定。限定關鍵字經過where。

   public class Student
    {
        public string Name { get; set; }
    }
        public void StudentShow<T>(T t) where T : Student
        {
            Console.WriteLine(string.Format("GenericsShow方法,參數類型{0}", t.GetType()));
        }
        public void GenericsShow<T>(T t)
        {
            Console.WriteLine(string.Format("GenericsShow方法,參數類型{0}", t.GetType()));
        }
        public void GenericsShow<T>(T t)
        {
            Console.WriteLine(string.Format("GenericsShow方法2,參數類型{0}", t.GetType()));
        }

須要注意的是,這裏使用了泛型重載,這個時候編譯是能夠正常經過的,但是注意了,調用的時候就出現問題了

爲何會這樣呢?由於泛型的類型參數在編譯器並不能肯定其類型,而重載時進行類型檢查發送在實例方法被調用時。

同時須要注意的是,當通常方法和泛型方法同時調用時,優先選擇通常方法,由於編譯器會進行類型推斷。

泛型的運用遠不止於此,它還支持泛型繼承、泛型接口、泛型類、泛型委託等。

相關文章
相關標籤/搜索