設(shè)計(jì)類(lèi)型(三)

設(shè)計(jì)類(lèi)型

事件

基礎(chǔ)概念

  1. 設(shè)計(jì)要公開(kāi)事件的類(lèi)型

    • 第一步: 定義類(lèi)型來(lái)容納所有需要發(fā)送給時(shí)間通知接收者的附加信息

      // 按實(shí)際需求定義類(lèi)型
      internal class XxEventArgs : EventArgs{
          public object Yy{get;set;}
      }
      
    • 第二步:定義事件成員

      public event EventHandler<T> xxEventHandler;
      
    • 第三步:定義負(fù)責(zé)引發(fā)事件的方法來(lái)通知事件的登記對(duì)象

      // 支持線(xiàn)程安全
      EventHandler<T> temp = Volatile.Read(ref xxEventHandler);
      if(temp != null){
          temp(this,xx);
      }
      
    • 第四步:定義方法將輸入轉(zhuǎn)化為期望的事件

      this.eventHandler += (o, e) => {
         Console.WriteLine("Hehhahah:"+e.HelloWorld);
      };
      
  1. 編譯器如何實(shí)現(xiàn)事件

    • TODO:待分析IL和底層實(shí)現(xiàn)代碼
  2. 顯示實(shí)現(xiàn)事件

        /// <summary>
        ///  顯示實(shí)現(xiàn)事件,優(yōu)化
        /// </summary>
        public  class B
        {
            private readonly EventSet _eventSet = new EventSet();
    
            protected EventSet EventSet { get { return _eventSet; } }
    
            protected static readonly EventKey _fooEventKey = new EventKey();
    
            public event EventHandler<XxEventArgs> Foo {
                add { _eventSet.Add(_fooEventKey, value);
                }
                remove { _eventSet.Remove(_fooEventKey, value); }
            }
    
            protected virtual void OnFoo(XxEventArgs e)
            {
                _eventSet.Raise(_fooEventKey, this, e);
            }
    
            public void SimulateFoo()
            {
                OnFoo(new XxEventArgs { });
            }
        }
    
        public class XxEventArgs : EventArgs {
            public override string ToString()
            {
                return "Hello,World";
            }
        }
    
        public sealed class EventKey { }
    
        public sealed class EventSet
        {
            // 私有字典維護(hù)EventKey -> 到Delegate的映射
            private readonly ConcurrentDictionary<EventKey, Delegate> _events = new ConcurrentDictionary<EventKey, Delegate>();
    
            public void Add(EventKey eventKey,Delegate handler)
            {
                // TODO 理解使用線(xiàn)性安全的字段是否還需要加同步操作
                // Monitor.Enter(_events);
                Delegate d;
                _events.TryGetValue(eventKey, out d);
                _events[eventKey] = Delegate.Combine(d, handler);
                // Monitor.Exit(_events);
            }
    
            public void Remove(EventKey eventKey,Delegate handler)
            {
                // Monitor.Enter(_events);
                Delegate d;
                if(_events.TryGetValue(eventKey,out d))
                {
                    d = Delegate.Remove(d, handler);
                    if (d != null)
                    {
                        _events[eventKey] = d;
                    }
                    else
                    {
                        _events.TryRemove(eventKey,out d);
                    }
                }
                // Monitor.Exit(_events);
            }
    
            public  void Raise(EventKey eventKey,Object sender,EventArgs e)
            {
                Delegate d;
                // Monitor.Enter(_events);
                _events.TryGetValue(eventKey, out d);
                //  Monitor.Exit(_events);
                if (d != null)
                {
                    d.DynamicInvoke(new object[] { sender,e});
                }
            }
        }
    
        public sealed class BDemo
        {
            public void Run()
            {
                B b = new B();
                b.Foo += B_Foo;
                b.SimulateFoo();
            }
    
            private void B_Foo(object sender, XxEventArgs e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    

泛型

基礎(chǔ)概念

  1. 開(kāi)放類(lèi)型和封閉類(lèi)型
    • 具有泛型類(lèi)型參數(shù)的類(lèi)型稱(chēng)為開(kāi)放類(lèi)型,CLR禁止構(gòu)造開(kāi)放類(lèi)型的任何實(shí)例
    • 為所有類(lèi)型參數(shù)都傳遞了實(shí)際的數(shù)據(jù)類(lèi)型,類(lèi)型就成為封閉類(lèi)型。
  2. 代碼爆炸

逆變和協(xié)變

? In C#, covariance and contravariance enable implicit reference conversion(隱式引用轉(zhuǎn)換 ) for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.

  1. 泛型類(lèi)型參數(shù)
    • 不變量(invariant):意味著泛型類(lèi)型參數(shù)不能更改。
    • 逆變量(contravariant):意味著泛型類(lèi)型參數(shù)可以從一個(gè)類(lèi)更改為他的某個(gè)派生類(lèi)。
    • 協(xié)變量(covariant):意味著泛型類(lèi)型參數(shù)可以從一個(gè)類(lèi)更改為她的某個(gè)基類(lèi)。
  2. 簡(jiǎn)而言之,協(xié)變性(covariance)指定返回的類(lèi)型的兼容性,逆變(contravariance)指定參數(shù)的兼容性。

案例

  1. 方法逆變和協(xié)變

    static object GetObject() { return null; }  
    static void SetObject(object obj) { }  
      
    static string GetString() { return ""; }  
    static void SetString(string str) { }  
      
    static void Test()  
    {  
        // 協(xié)變
        // Covariance. A delegate specifies a return type as object,  
        // but you can assign a method that returns a string.  
        Func<object> del = GetString;  
      
        // 逆變
        // Contravariance. A delegate specifies a parameter type as string,  
        // but you can assign a method that takes an object.  
        Action<string> del2 = SetObject;  
    }  
    
  2. Using Variance in Interfaces for Generic Collections (C#)

    // Simple hierarchy of classes.  
    public class Person  
    {  
        public string FirstName { get; set; }  
        public string LastName { get; set; }  
    }  
      
    public class Employee : Person { }  
      
    class Program  
    {  
        // The method has a parameter of the IEnumerable<Person> type.  
        public static void PrintFullName(IEnumerable<Person> persons)  
        {  
            foreach (Person person in persons)  
            {  
                Console.WriteLine("Name: {0} {1}",  
                person.FirstName, person.LastName);  
            }  
        }  
      
        public static void Test()  
        {  
            IEnumerable<Employee> employees = new List<Employee>();  
      
            // You can pass IEnumerable<Employee>,   
            // although the method expects IEnumerable<Person>.  
      
            PrintFullName(employees);  
      
        }  
    }
    
  3. Convariance In Delegate

    // Event handler that accepts a parameter of the EventArgs type.  
    private void MultiHandler(object sender, System.EventArgs e)  
    {  
        label1.Text = System.DateTime.Now.ToString();  
    }  
      
    public Form1()  
    {  
        InitializeComponent();  
      
        // You can use a method that has an EventArgs parameter,  
        // although the event expects the KeyEventArgs parameter.  
        this.button1.KeyDown += this.MultiHandler;  
      
        // You can use the same method   
        // for an event that expects the MouseEventArgs parameter.  
        this.button1.MouseClick += this.MultiHandler;  
      
    }
    
  4. Covariance In Delegate

    class Mammals {}  
    class Dogs : Mammals {}  
      
    class Program  
    {  
        // Define the delegate.  
        public delegate Mammals HandlerMethod();  
      
        public static Mammals MammalsHandler()  
        {  
            return null;  
        }  
      
        public static Dogs DogsHandler()  
        {  
            return null;  
        }  
      
        static void Test()  
        {  
            HandlerMethod handlerMammals = MammalsHandler;  
      
            // Covariance enables this assignment.  
            HandlerMethod handlerDogs = DogsHandler;  
        }  
    }
    

    約束

    1. 主要約束
      • 可以是代表非密封類(lèi)的一個(gè)引用類(lèi)型。
      • 一個(gè)制定的類(lèi)型實(shí)參要么是與約束類(lèi)型相同的類(lèi)型,或者約束類(lèi)型派生的類(lèi)型。
      • 兩個(gè)特殊的主要約束:class和struct。
    2. 次要約束
      • 可以指定零個(gè)或者多個(gè)次要約束,次要約束代表接口類(lèi)型。
    3. 構(gòu)造器約束
      • new ()

參考資料

  1. wikipedia's Covariance and contravariance (computer science)
  2. 大牛Eric Lippert
  3. 微軟MSDN文檔-逆變和協(xié)變

接口

基礎(chǔ)概念

  1. EIMI(顯示接口方法實(shí)現(xiàn))
  2. 用顯示接口方法實(shí)現(xiàn)類(lèi)增強(qiáng)編譯時(shí)類(lèi)型安全性
    /// <summary>
    /// 用顯示接口方法增強(qiáng)編譯時(shí)類(lèi)型安全性
    /// </summary>
    struct D : IComparable
    {
        private Int32 _x;

        public D(Int32 x)
        {
            this._x = x;
        }

        public int CompareTo(D o)
        {
            return o._x - this._x;
        }

        Int32 IComparable.CompareTo(object obj)
        {
            return CompareTo((D)obj);
        }
    }
  1. 顯示接口方法實(shí)現(xiàn)的弊端

        class D1 : IComparable
        {
            Int32 IComparable.CompareTo(object obj)
            {
                return 0;
            }
        }
    
        class D2 : D1, IComparable
        {
            public Int32 CompareTo(Object o) {
                // base.CompareTo(o); // 無(wú)法讀取顯示聲明的接口
                // 改進(jìn)辦法,可以在父類(lèi)增加虛方法,子類(lèi)覆寫(xiě)
                return 0;
            }
        }
    

設(shè)計(jì):基類(lèi)還是接口

  1. IS-A對(duì)比CAN-DO關(guān)系,如果派生類(lèi)和及類(lèi)型建立不起IS-A關(guān)系,不用基類(lèi)而用接口。
  2. 易用性
  3. 一致性實(shí)現(xiàn)
  4. 版本控制
  5. 設(shè)計(jì)建議:
    • 建議,定義接口和一個(gè)實(shí)現(xiàn)它的部分(abstract)或者全部方法(virtual)的基類(lèi),提供最大的靈活性。

接口的作用

  • 后續(xù)補(bǔ)上,或者后續(xù)實(shí)際應(yīng)用場(chǎng)景中說(shuō)明。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容