设计类型(三)

设计类型

事件

基础概念

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

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

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

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

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

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

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

        /// 
        ///  显示实现事件,优化
        /// 
        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 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 _events = new ConcurrentDictionary();
    
            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 del = GetString;  
      
        // 逆变
        // Contravariance. A delegate specifies a parameter type as string,  
        // but you can assign a method that takes an object.  
        Action 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 type.  
        public static void PrintFullName(IEnumerable persons)  
        {  
            foreach (Person person in persons)  
            {  
                Console.WriteLine("Name: {0} {1}",  
                person.FirstName, person.LastName);  
            }  
        }  
      
        public static void Test()  
        {  
            IEnumerable employees = new List();  
      
            // You can pass IEnumerable,   
            // although the method expects IEnumerable.  
      
            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 ()
  5. 参考资料

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

    接口

    基础概念

    1. EIMI(显示接口方法实现)
    2. 用显示接口方法实现类增强编译时类型安全性
        /// 
        /// 用显示接口方法增强编译时类型安全性
        /// 
        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)的基类,提供最大的灵活性。

    接口的作用

    • 后续补上,或者后续实际应用场景中说明。

    你可能感兴趣的:(设计类型(三))