美文网首页
设计类型

设计类型

作者: 沈_ac89 | 来源:发表于2019-03-03 23:03 被阅读0次

设计类型

一、类型基础

常见问题

  1. 面向对象语言的特点?
  • 什么是面向对象语言

    面向对象语言(Object-Oriented Language)是一类以对象作为基本程序结构单位的程序设计语言,指用于描述的设计是以对象为核心,而对象是程序运行时刻的基本成分。
    语言中提供了类、继承等成分,有识认性、多态性、类别性和继承性四个主要特点。

  • 面向对象语言的特点

    面向对象语言刻画客观系统较为自然,便于软件扩充与复用。有四个主要特点:

    (1)识认性,系统中的基本构件可识认为一组可识别的离散对象;
    (2)类别性,系统具有相同数据结构与行为的所有对象可组成一类;
    (3)多态性,对象具有惟一的静态类型和多个可能的动态类型;
    (4)继承性,在基本层次关系的不同类中共享数据和操作。
    其中,前三者为基础,继承是特色。四者(有时再加上动态绑定)结合使用,体现出面向对象语言的表达能力。

  1. var o = new object(); // CLR背后帮我们做了哪些事情?
    
    • 计算类型及其所有基类型(一直到System.Object)中所有的实例字段需要的字节数。注意,堆上每个对象都需要一些额外的成员,当中包含额外成员(类型对象指针(type object pointer)和同步块索引(sync block index))占用的字节数。
    • 从托管堆中分配类型要求的字节数。
    • 初始化对象的“类型对象指针”和“同步块索引”成员。
    • 非静态类初始化:调用类型的实例构造器,传递在new调用中指定的实参,一般构造函数中会自动生成代码来自动调用基类构造器。
    • 返回指向该对象的一个引用(或指针)。
  2. is和as使用存在的坑点。

  • as

    • 针对引用对象或者可为null的对象使用。

    • as不对改变对象的类型

    • 类型转换推荐写法

      // @不推荐
      if (o is Employee){
          Employee e = (Employee)o;
      }
      // @推荐,简化以上写法,优化性能
      Employee e = o as Employee;
      if(e != null){
          
      }
      
  1. 命名空间和程序集的关系?
  • 命名空间和程序集(实现类型的文件)不一定相关,同一个命名空间中的类型可能在不同程序集中实现
  1. “运行时”的相互关系(参考堆栈的内存分配
  • 线程创建时默认分配1MB的栈。

专业名词

类型对象指针

  • 指向类型对象存储的地址,假如有一个类型Person,它在堆中有一块区域存储它内部的字段和成员以及两个额外成员(类型对象指针、 同步块索引 ),类型对象的类型对象指针指向的是System.Type类型对象的地址
  • 因为Person类型在内存中相对于System.Type也是作为一个对象存在的,System.Type类型也是一个类型对象,它的类型对象指针指向本身;
  • 调用Object.GetType()方法返回的是类型对象指针的地址。

同步块

来自横刀天笑syncblock_thumb.png
  • 同步块机制

    · 在.NET被加载时初始化同步块数组。
    · 每一个被分配在堆上的对象都会包含两个额外的字段,其中一个存储类型指针,而另外一个就是同步块索引,初始时被赋值为-1。
    · 当一个线程试图使用该对象进入同步时,会检查该对象的同步索引。如果索引为负数,则会在同步块数组中寻找或者新建一个同步块,并且把同步块的索引值写入该对象的同步索引中。如果该对象的同步索引不为负值,则找到该对象的同步块并且检查是否有其他线程在使用该同步块,如果有则进入等待状态,如果没有则申明使用该同步块。

  • 同步块的概念

    同步块是指.NET维护的同步块数组中的某个元素,负责提供线程同步的操作,当某个线程拥有了某个同步块时,其他线程就在试图访问该同步块时进入等待状态。

同步块索引

​ 同步索引是每个堆内对象都会分配的一个字段。

参考内容

二、基元类型、引用类型和值类型

基元类型

编译器直接支持的类型成为基元类型(Primitive Type),能直接映射到FCL中存在的类型。

常见问题

  • System.Decimal没有响应的IL指令来处理Decimal值,无法检测溢出情况。
  • UnChecked和Checked

引用类型和值类型

常见问题

声明为值类型的前提条件

​ 优势是不作为对象在托管对上的分配。

  • 必选条件

    1. 类型具有基元类型的行为,是十分简单的类型,没有成员会修改类型的任何实例字段。
    2. 类型不需要从其他任何类型继承。
    3. 类型也不派生出其他任何类型。
  • 任一条件

    1. 类型的实例较小(16字节或更小)
    2. 类型的实例比较大(大于16字节),但不作为方法实参传递,也不从方法返回。
字段布局

​ 使用StructLayoutAttribute特性,FieldOffsetAttribute特性指出字段第一个字节举例实例起始处的偏移量。

  • 然而在.net托管环境中,CLR提供了更自由的方式来控制struct中Layout:我们可以在定义struct时,在struct上运用StructLayoutAttribute特性来控制成员的内存布局。默认情况下,struct实例中的字段在栈上的布局(Layout)顺序与声明中的顺序相同,即在struct上运用[StructLayoutAttribute(LayoutKind.Sequential)]特性,这样做的原因是结构常用于和非托管代码交互的情形。
  • 如果我们正在创建一个与非托管代码没有任何互操作的struct类型,我们很可能希望改变C#编译器的这种默认规则,因此LayoutKind除了Sequential成员之外,还有两个成员Auto和Explicit,给StructLayoutAttribute传入LayoutKind.Auto可以让CLR按照自己选择的最优方式来排列实例中的字段;传入LayoutKind.Explicit可以使字段按照我们的在字段上设定的FieldOffset来更灵活的设置字段排序方式,但这种方式也挺危险的,如果设置错误后果将会比较严重。
装箱和拆箱
  • 读懂相关IL代码,参考IL章节。
Equals和GethashCode重载
Equals
  • 重写Equals方法参考范例
    /// <summary>
    /// 自定义对象类型重写
    /// </summary>
    public class A : IEquatable<A>
    {

        public string Id { get; set; }

        // 实现IEquatable接口
        public bool Equals(A other)
        {
            // 为空,肯定不相等
            if (ReferenceEquals(null, other)) return false;
            // 同一对象,必然相等
            if (ReferenceEquals(this, other)) return true;
            // 比较各个字段
            if (!string.Equals(this.Id, other.Id, StringComparison.InvariantCulture))
            {
                return false;
            }
            return true;
        }

        public override bool Equals(object obj)
        {
            //this非空,obj如果为空,则返回false
            if (ReferenceEquals(null, obj)) return false;
            //如果为同一对象,必然相等
            if (ReferenceEquals(this, obj)) return true;
            //如果类型不同,则必然不相等
            if (obj.GetType() != this.GetType()) return false;
            //调用强类型对比
            return Equals((A)obj);
        }

        public override int GetHashCode()
        {
            return this.Id == null ? 0 : StringComparer.InvariantCulture.GetHashCode(this.Id);
        }

        // 重写==号
        public static bool operator ==(A left, A right)
        {
            return Equals(left, right);
        }

        // 重载!=号
        public static bool operator !=(A left, A right)
        {
            return !Equals(left,right);
        }

    }

    /// <summary>
    /// ValueType类型比较
    /// 注意:int?这种还是ValueType类型,注意**可为空**)。
    /// </summary>
    public struct B : IEquatable<B>
    {
        private string _id;

        public B(string id)
        {
            this._id = id;
        }

        public bool Equals(B other)
        {
            // 比较字段是否相等
            return string.Equals(this._id,other._id,StringComparison.InvariantCulture);
        }

        public override bool Equals(object obj)
        {
            // 为空不相等
            if (ReferenceEquals(null, obj)) return false;
            return obj is B && Equals((B)obj);
        }

        public override int GetHashCode()
        {
            return (this._id != null) ? StringComparer.InvariantCulture.GetHashCode(_id) : 0;
        }

        public static bool operator ==(B left,B right)
        {
            if (left == null && right == null)
                return true;
            if (left == null)
                return right.Equals(left);
            return left.Equals(right);
        }

        public static bool operator !=(B left, B right)
        {
            if (left == null && right == null)
                return false;
            if (left == null)
                return !right.Equals(left);
            return !left.Equals(right);
        }
    }

  • 定义自己类型,重写Equals要符合相等性的4个特征:
    • Equals必须自反,x.Equals(x)肯定返回true。
    • Equals必须对称,x.Equals(y)和y.Equals(x)返回相同的值。
    • Equals必须可传递,x.Equals(y)和y.Equals(z)返回true,则x.Equals(z)肯定也返回true。
    • Equals必须一致,比较的两个值不变,Equals返回值也不能变。
  • 实现IEquatable<string>,实现类型更加安全的Equals方法。
  • 重写==和!=等操作符方法。
HashCode
  • 重写hashcode的时候,遵守以下的规则:
    • 计算算法要提供良好的随机分布,使哈希表获得最佳性能和减少hash冲突的可能性。
    • 可在计算中调用基类的GetHashCode方法,并包含它的返回值。但是一般不要调用Object和ValueType的GetHashCode方法。
    • 算法至少使用一个实例的字段。
    • 理想情况下,算法使用的字段应该不可变(immutable),换句话说,字段在构造初始化后,在对象生存期“永不言变”。
    • 算法执行速度尽量快。
    • 包含相同值的两个对象应该返回相同的哈希码。
dynamic(比较重要,后续补充完整)

Visual C# 2010 引入了一个新类型 dynamic。 该类型是一种静态类型,但类型为 dynamic 的对象会跳过静态类 型检查。 大多数情况下,该对象就像具有类型 object 一样。 在编译时,将假定类型化为 dynamic 的元素支持任何操作。 因此,您不必考虑对象是从 COM API、从动态语言(例如 IronPython)、从 HTML 文档对象模型 (DOM)、从反射还是从程序中的其他位置获取自己的值。 但是,如果代码无效,则在运行时会捕获到错误。

  • 应用场景
    • 动态类型转换(需要考虑性能)
    • 动态语言运行时(DLR,例如 IronPython 和 IronRuby 等动态编程语言的实现)
    • COM 互操作
参考范例(继续改进)
  • 覆写DynamicObject
    /// <summary>
    /// 适用于4.5版本或以上
    /// </summary>
    public class StaticMemberDynamicWrapper : DynamicObject
    {
        private readonly TypeInfo _typeInfo;

        public StaticMemberDynamicWrapper(Type type)
        {
            this._typeInfo = type.GetTypeInfo();
        }

        public override IEnumerable<string> GetDynamicMemberNames()
        {
            return this._typeInfo.DeclaredMembers.Select(p => p.Name);
        }

        /// <summary>
        /// 获取字段或者属性的值
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            result = null;
            var field = FindField(binder.Name);
            if (field != null)
            {
                result = field.GetValue(null);
                return true;
            }
            var prop = FindProperty(binder.Name, true);
            if (prop != null)
            {
                result = prop.GetValue(null,null);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 设置属性或者字段值
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            var field = FindField(binder.Name);
            if (field != null)
            {
                field.SetValue(null, value);
                return true;
            }
            var prop = FindProperty(binder.Name, true);
            if (prop != null)
            {
                prop.SetValue(null,value,null);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            MethodInfo methodInfo = FindMethodInfo(binder.Name,args.Select(p=> p.GetType()).ToArray());
            if (methodInfo == null)
            {
                result = null;
                return false;
            }
            result = methodInfo.Invoke(null, args);
            return true;
        }

        /// <summary>
        /// 寻找方法
        /// </summary>
        /// <param name="name"></param>
        /// <param name="types"></param>
        /// <returns></returns>
        private MethodInfo FindMethodInfo(string name, Type[] types)
        {
            return _typeInfo.DeclaredMethods.FirstOrDefault(p => p.Name == name && p.IsPublic && p.IsStatic && ParametersMatch(p.GetParameters(), types));
            
        }

        /// <summary>
        /// 方法参数类型比较
        /// </summary>
        /// <param name="parameterInfos"></param>
        /// <param name="types"></param>
        /// <returns></returns>
        private bool ParametersMatch(ParameterInfo[] parameterInfos, Type[] types)
        {
            if (parameterInfos == null || parameterInfos.Length <= 0)
            {
                return false;
            }
            for (int i = 0; i < types.Length; i++)
            {
                if (parameterInfos[i].ParameterType != types[i])
                    return false;
            }
            return true;
        }

        /// <summary>
        /// 寻找属性
        /// </summary>
        /// <param name="name"></param>
        /// <param name="get"></param>
        /// <returns></returns>
        private PropertyInfo FindProperty(string name, bool get)
        {
            if (get)
            {
                return _typeInfo.DeclaredProperties.FirstOrDefault(p => p.Name == name && p.GetMethod != null && p.GetMethod.IsPublic && p.GetMethod.IsStatic);
            }
            return _typeInfo.DeclaredProperties.FirstOrDefault(p => p.Name == name && p.SetMethod != null && p.SetMethod.IsPublic && p.SetMethod.IsStatic);
        }

        /// <summary>
        /// 寻找字段
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private FieldInfo FindField(string name)
        {
            return this._typeInfo.DeclaredFields.FirstOrDefault(p => p.IsPublic && p.IsStatic && p.Name.Equals(name));
        }

    }
    
    /// <summary>
    /// 测试
    /// </summary>
    public class DyamicObjectTest
    {
        public void Run()
        {
            dynamic stringType = new StaticMemberDynamicWrapper(typeof(string));
            var result = stringType.Concat("A", "B");
            // 运行报错
            //dynamic stringType2 = "";
            //var result2 = stringType2.Concat("B", "C");
            //Console.Write(result2);
            dynamic sampleObject = new ExpandoObject();
            sampleObject.Concat = (Func<string,string,string>)((c, d) => { return c + d; });
            var result3 = sampleObject.Concat("A", "B");
            Console.WriteLine(result3);
            Console.Write(result);
        }
    }

参考内容

相关文章

  • 设计类型

    设计类型 一、类型基础 常见问题 面向对象语言的特点? 什么是面向对象语言面向对象语言(Object-Orient...

  • 设计模式的原则及UML类图

    设计模式类型 设计模式分为三种类型: 创建型模式 结构型模式 行为型模式 设计原则 软件开发的23种设计模式,就是...

  • 设计模式类型

    1创建模式 这些模式提供了一种创建对象的同时隐藏创建逻辑的方式,而不是使用new运算符直接实例化对象。这使得程序在...

  • 设计类型(二)

    设计类型 类型和成员基础 基础概念 友元程序集// 在程序集中的AssemblyInfo.cs中,标记友元程序集名...

  • 设计类型(三)

    设计类型 事件 基础概念 设计要公开事件的类型第一步: 定义类型来容纳所有需要发送给时间通知接收者的附加信息// ...

  • 设计模式类型

    1.创建者模式 提供一种在创建对象的同时,隐藏创建逻辑的方式,而不是使用new运算符直接实例化对象。 工厂模式(F...

  • App设计类型

    APP类型 app分类 1、native app(中文名:原生app) 百度百科定义:基于智能手机本地操作系统如i...

  • Java中的设计模式有哪些?

    一、设计模式的分类 设计模式是不分语言的,大概可总结为设计模式有3种类型及23种模式! 设计模式主要分三个类型:创...

  • 2019-05-31 关于mysql数据库插入时间不对的问题

    最近做一个项目,设计时间的插入 以前都是采用long 类型采用时间戳类型的,没什么问题,但是设计具体时间类型 像 ...

  • 《C++ Primer Plus》第3章学习笔记

    内容思维导图 内容要点 面向对象编程(OOP)的本质是设计并扩展自己的数据类型。设计自己的数据类型就是让类型与数据...

网友评论

      本文标题:设计类型

      本文链接:https://www.haomeiwen.com/subject/lsrpuqtx.html