设计类型(三)

设计类型

事件

基础概念

  1. 设计要公开事件的类型

    • 第一步: 定义类型来容纳所有需要发送给时间通知接收者的附加信息

      // 按实际需求定义类型
      internal class XxEventArgs : EventArgs{
          public object Yy{get;set;}
      }
      
    • 第二步:定义事件成员

      public event EventHandler<T> xxEventHandler;
      
    • 第三步:定义负责引发事件的方法来通知事件的登记对象

      // 支持线程安全
      EventHandler<T> temp = Volatile.Read(ref xxEventHandler);
      if(temp != null){
          temp(this,xx);
      }
      
    • 第四步:定义方法将输入转化为期望的事件

      this.eventHandler += (o, e) => {
         Console.WriteLine("Hehhahah:"+e.HelloWorld);
      };
      
  1. 编译器如何实现事件

    • TODO:待分析IL和底层实现代码
  2. 显示实现事件

        /// <summary>
        ///  显示实现事件,优化
        /// </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
        {
            // 私有字典维护EventKey -> 到Delegate的映射
            private readonly ConcurrentDictionary<EventKey, Delegate> _events = new ConcurrentDictionary<EventKey, Delegate>();
    
            public void Add(EventKey eventKey,Delegate handler)
            {
                // TODO 理解使用线性安全的字段是否还需要加同步操作
                // 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());
            }
        }
    

泛型

基础概念

  1. 开放类型和封闭类型
    • 具有泛型类型参数的类型称为开放类型,CLR禁止构造开放类型的任何实例
    • 为所有类型参数都传递了实际的数据类型,类型就成为封闭类型。
  2. 代码爆炸

逆变和协变

​ In C#, covariance and contravariance enable implicit reference conversion(隐式引用转换 ) for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.

  1. 泛型类型参数
    • 不变量(invariant):意味着泛型类型参数不能更改。
    • 逆变量(contravariant):意味着泛型类型参数可以从一个类更改为他的某个派生类。
    • 协变量(covariant):意味着泛型类型参数可以从一个类更改为她的某个基类。
  2. 简而言之,协变性(covariance)指定返回的类型的兼容性,逆变(contravariance)指定参数的兼容性。

案例

  1. 方法逆变和协变

    static object GetObject() { return null; }  
    static void SetObject(object obj) { }  
      
    static string GetString() { return ""; }  
    static void SetString(string str) { }  
      
    static void Test()  
    {  
        // 协变
        // 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. 主要约束
      • 可以是代表非密封类的一个引用类型。
      • 一个制定的类型实参要么是与约束类型相同的类型,或者约束类型派生的类型。
      • 两个特殊的主要约束:class和struct。
    2. 次要约束
      • 可以指定零个或者多个次要约束,次要约束代表接口类型。
    3. 构造器约束
      • new ()

参考资料

  1. wikipedia's Covariance and contravariance (computer science)
  2. 大牛Eric Lippert
  3. 微软MSDN文档-逆变和协变

接口

基础概念

  1. EIMI(显示接口方法实现)
  2. 用显示接口方法实现类增强编译时类型安全性
    /// <summary>
    /// 用显示接口方法增强编译时类型安全性
    /// </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. 显示接口方法实现的弊端

        class D1 : IComparable
        {
            Int32 IComparable.CompareTo(object obj)
            {
                return 0;
            }
        }
    
        class D2 : D1, IComparable
        {
            public Int32 CompareTo(Object o) {
                // base.CompareTo(o); // 无法读取显示声明的接口
                // 改进办法,可以在父类增加虚方法,子类覆写
                return 0;
            }
        }
    

设计:基类还是接口

  1. IS-A对比CAN-DO关系,如果派生类和及类型建立不起IS-A关系,不用基类而用接口。
  2. 易用性
  3. 一致性实现
  4. 版本控制
  5. 设计建议:
    • 建议,定义接口和一个实现它的部分(abstract)或者全部方法(virtual)的基类,提供最大的灵活性。

接口的作用

  • 后续补上,或者后续实际应用场景中说明。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容