在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
C#简介: eg: C#1,奠定了C#类型系统的基础,规定了引用类型和值类型的概念及委托 C#2,泛型机制、匿名方法、迭代器、可空类型 C#3,自动实现的属性、匿名类型、扩展方法、Lambda表达式、LINQ C#4,命名参数和可选参数、泛型接口、泛型委托的协变和逆变 C#5,异步和等待 C#6,自动属性初始化以及内建字符串Interpolated String取代string.Format()函数 ... 每种编程语言都有某种形式的类型系统,C#的类型系统是静态的、安全的,大部分情况下是显式的。 C#要求所有的类型都从 System.Object 类派生,这样保证所有类型都拥有一套最基本的由基类System.Object声明的方法——4个公共方法,2个受保护方法 1.Equals():若两个对象相等则返回true,否则返回false 静态类型系统“静态”这个词是用来描述表达式的编译时类型,编译器在编译时需要检查和使用这些静态的、不变的数据来确定哪些操作是合法的。 C#是静态的类型系统,在C#中声明一个变量时所确定的类型,便是该变量在编译时的类型,意味着在C#代码中,每一个变量都有着一个特定的类型并且该类型在编译时是确定了的
静态类型系统中的动态行为
动态行为一: 在声明变量 temp 时,其定义的类型为 Product,即变量 temp 在编译时的类型是 Product; 动态行为二: 在调用虚方法时,其实际实现的是依赖于所调用对象的类型 PC. 静态类型系统中的显式类型显式类型是指在声明变量时必须显式的制定变量的类型
即,每个变量在声明时都显式的确定其类型 静态类型系统中的隐式类型隐式类型是指在声明变量时不指明其类型,而是允许编译器根据变量的用途来推断变量的类型,但是,隐式声明的变量仍然是静态类型且在编译时其类型已经确定。 C#3 中引入 var 关键字 来表示 隐式类型,使用 var 来声明变量,编译器将在编译时对该变量进行类型推断 需要注意的是,在引入var 关键字之后,程序员可以使用 var 关键字来简化开发流程,但是,C#3 仍然是静态类型的编程语言,用 var 声明的变量类型是由编译器推断出的,在编译时是已经被确定了的。
注意: 区分显式类型与隐式类型之间的不同,仅仅在静态类型系统的编程语言中才有意义。 动态类型动态类型与静态类型相对,即变量类型的确定是在运行时才确定的类型。 面对动态类型,C#编译器只需完成检查语法是否正确,但无法确定所调用的方法或者属性是否正确(因为只有在运行时才知道它们的类型) 其中 dynamic 关键字不同于C#3 中的 var 关键字, var 并不是类型,var 只是一个指令,它告诉编辑器根据变量的初始化表达式来推断类型; dynamic 是类型,但是,在编译时dynamic 不属于CLR类型,运行时则一定是CLR类型中的一种。
动态类型总结 一、使用动态类型对于开发人员来说没有了IDE的智能提示,但是,动态类型减少了强制类型转换的代码,增强了代码的可读性 二、使用动态类型可以在C#静态语言中调用Python等动态语言 类型安全类型安全,本质是有关类型操作的一种规范,即不能一种类型当做另外一种类型,除非,其间存在类型转换关系。 C#是类型安全的,允许合理的类型转换,当派生类向基类的转换时,该转换可以认为是一种安全的类型转换,因此,在C#中无需特殊的语法进行向基类的隐式转换。 当需要将一个对象转换成他的某个派生类时,则需要使用显示转换来提供足够的信息给编译器。
同时,在程序运行时也会进行类型转换的检查操作。 值类型和引用类型.Net Framework有值类型和引用类型的概念,由于引用类型的复杂性、大小和使用方式,引用类型会需要在内存中存在一段时间,所以当垃圾回收器Garbage Collector/GC在执行标记-清除算法时,只有引用类型需要被标记,引用类型通常分配在堆上,而值类型可以分配到栈或者堆上 引用类型引用类型的声明 根据ECMA的C#语言规范,任何被称为类的类型都是引用类型 通常可以使用以下3个关键字来声明一个自定义的引用类型:
C#中内建的引用类型:
引用类型的创建 C#中的引用类型总是从托管堆中进行分配,所有的引用类型都需要使用 new操作符创建。
使用new操作符创建引用类型背后的步骤: 第一步:计算所需的内存空间;new操作符将会计算目标类型加上包括System.Object在内的所有基类重定义的所有实例字段所需要的字节数,除此之外,还需要一些额外的信息需要在托管堆中分配空间,例如:类型对象指针和同步索引块。 第二步:在托管堆上分配所需的内存空间;完成所需内存空间的计算之后,分配所有的字节为0。 第三步:初始化对象的类型对象指针以及同步索引块; 第四步:调用类型的实例构造器;传递在使用new操作符时的实际参数,此时,编译器会在构造器中自动调用当前类型所有基类的构造器,从调用所有类型的基类System.Object构造器开始(该构造器仅仅是返回,无其他逻辑操作),到当前子类的构造器结束,每个类型的构造器都负责初始化该类型定义的实例字段 第五步:最后会返回一个指向新建对象的引用;即声明的变量只是一个指向该类型对象的引用,而非对象本身 值类型变量值分配的位置与该变量声明的位置有关: 局部变量的值总是存储在线程栈上,实例变量的值与实例本身一起存储在实例存储的地方,引用类型实例总是存储在堆上。 值类型的变量直接包含其值,值类型的实例一般分配在线程栈上,不受垃圾回收机制GC的影响,一些情况下分配在托管堆上。 值类型大体上可以分为 结构 和 枚举 两类: 其中,结构大体上又可以分为以下3种:
所有结构都是派生自抽象类型System.ValueType,包括枚举的基类System.Enum都是派生自System.ValueType,且System.ValueType派生自System.Object
值类型的new操作符: 在声明一个值类型的实例时,使用new关键字是告诉编译器该实例已经被初始化; 装箱与拆箱由于值类型不作为对象在托管堆中分配、不受垃圾回收机制的影响、不需要使用引用来引用,但是,有时我们就是需要使用一个引用而不是值类型的值,此时就需要值类型的装箱。
以上代码中ArrayList的Add方法的参数类型是Object类型,而实际类型为Phone结构体,为了是代码正常运行,Phone结构体的实例p1此时必须转换成真正在托管堆上分配的对象,并且获得该对象的引用。 值类型实例装箱背后的步骤: 第一步:在托管堆中分配内存,分配的内存空间除了值类型各个字段所需的内存之外,还需要加上托管堆所有对象都需要有的两个额外成员——类型对象指针和同步索引块所需的内存。 第二步:将值类型的字段复制到新分配的堆内存中。 第三步:返回对象地址,即对象的引用 装箱后创建对象的地址会返回给Add方法,并且该对象将会在托管堆中直到被GC回收。
拆箱及复制背后的步骤: 第一步:获取已经装箱的对象各个字段的地址,即拆箱 第二步:将各个字段的值从托管堆上复制到线程栈上的值类型实例中,即复制 REF 深入理解C#、C#高级编程、C#游戏脚本编程、Effective C# |
请发表评论