枚举|标志枚举+|(或)和&(与)运算|类型转换|值类型和引用类型|传参|异常|垃圾回收

枚举部分

  enum 关键字用于声明枚举,即一种由一组称为枚举数列表的命名常量组成的独特类型。程序员

  一般状况下,最好是在命名空间内直接定义枚举,以便该命名空间中的全部类都可以一样方便地访问它。 可是,还能够将枚举嵌套在类或结构中。默认状况下,第一个枚举数的值为 0,后面每一个枚举数的值依次递增 1。数据库

  例1:编程

  //此枚举的默认值是从0开始数组

  enum Days {Sat, Sun, Mon, Tue, Wed, Thu, Fri};安全

  例2:网络

  //此枚举的默认值是从1开始,下标为3的tue值为7,从下标3开始的值依次加1。数据结构

  enum Days {Sat=1, Sun, Mon, Tue=7, Wed, Thu, Fri};ide

  每种枚举类型都有基础类型,该类型能够是除 char 之外的任何整型。 枚举元素的默认基础类型为 int。 要声明另外一整型枚举(如 byte),请在标识符以后紧跟类型,而后再使用冒号,如例3所示。准许使用的枚举类型有 byte、sbyte、short、ushort、int、uint、long 或 ulong。enum E 的默认值为表达式 (E)0 生成的值。函数

  //例3:性能

  //此枚举的类型是byte,值从0到255

  enum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

  枚举数的名称中不能包含空白。

  基础类型指定为每一个枚举数分配的存储大小。 可是,从 enum 类型到整型的转换须要用显式类型转换来完成。 例如的语句使用强制转换(从 enum 转换为 int)将枚举数 Sun 赋值给一个 int 类型的变量,例4所示。

  例4:

  int x = (int)Days.Sun;

  能够将任意值赋给枚举类型的枚举数列表中的元素,也可使用计算值,如例5。

  例5:

  enum MachineState{ PowerOff = 0, Running = 5, Sleeping = 10, Hibernating = Sleeping + 5}

  与任何常量同样,对枚举中各个值的全部引用在编译时均将转换为数值文本。

  注意:默认输出的是文本值,转换后变为数字。只要给枚举赋予的值在枚举变量容纳枚举定义的值以内,就能够给枚举赋予数字(建议不要这样作)。

  标志性枚举

  建立位标志枚举的方法是应用 System.FlagsAttribute 特性并适当定义一些值,以即可以对这些值执行 AND、OR、NOT 和 XOR 按位运算。 在位标志枚举中包含一个值为零(表示“未设置任何标志”)的命名常量。若是零值不表示“未设置任何标志”,则请不要为标志指定零值。

  [Flags]

  enum Days2{ None = 0x0, Sunday = 0x1, Monday = 0x2, Tuesday = 0x4, Wednesday = 0x8,    Thursday = 0x10, Friday = 0x20, Saturday = 0x40}

  class MyClass{  Days2 meetingDays = Days2.Tuesday | Days2.Thursday;}

  为枚举建立新方法

  可使用扩展方法添加特定于某个特定枚举类型的功能。

  请参见:http://msdn.microsoft.com/zh-cn/library/bb383974.aspx

  枚举小结

  获取当前枚举的基础值类型:Enum.GetLinderlyingType(typeof(MyColor));

  枚举转换:

  将枚举转换为数值:(int)Mycolor.Red

  将枚举转换为字符串:MyColor.Red.ToString()

  将字符串转换为枚举:MyColor mc=(MyColor)Enum.Parse(typeof(Type),"value")

 标志枚举

  枚举加上【Flags】就成为标志枚举,加上这个特性后枚举类型.ToString()返回的就是文字形式,不是数字。

  代码以下:

  FileAttributes classfileAttributes= FileAttributes.Hidden|FileAttributes.ReadOnly;

  Console.WriteLine(classfileAttributes.ToString());//结果是ReadOnly,Hidden;

  转到源码查看:

  咱们修改枚举类,仍是图片上的代码,只是去掉全部标记。还用上面的代码,你会发现输出的值是一个数字而不是字符串。

首先咱们要知道,普通枚举中的每一个值都是互斥的(输出结果只能是一个),而标志枚举中的每一个值是能够互相组合的,输出结果能够是多个,多个时值之间用逗号隔开,且计算后的结果是字符而不是数字。(这里说的计算是与或计算,&、|)。固然标志枚举用的string类型(显示的),而计算时用的是赋的值。

  该给枚举赋什么样的值呢?

  标志枚举规赋值尽可能用2的次方进行标记赋值。查看微软的代码会发现:微软用2的次方给枚举赋值。

标志枚举有什么特色呢?

  1. 单个枚举组合成集合,能够同时输出多个值。用|运算能够枚举值组合在一块儿,以下。

  FileAttributes.ReadOnly| FileAttributes.Hidden进行计算时,把它进行转换:

  FileAttributes.ReadOnly :00 00 00 01  →1

  FileAttributes.Hidden   : 00 00 00 10  →2

  |运算——————————————————————————

                                           00 00 00 11   → classfileAttributes的集合

  这ClassFileAttribute就是咱们取值的集合,而后再通过转换输出输出,输出这个组合的值。

 

  2.判断枚举集合中是否包含某个枚举值。例如:判断FileAttributes.ReadOnly是否在classfileAttributes集合中。

使用代码:If( (classfileAttributes&FileAttributes.ReadOnly)== FileAttributes.ReadOnly)//代码

  计算过程:

  classfileAttributes       :    00 00 00 11

  FileAttributes.ReadOnly  :00 00 00 10

  &运算————————————————————

                                            00 00 00 10  →      FileAttributes.ReadOnly

  若是FileAttributes.ReadOnly在classfileAttributes集合中,则计算结果为这个值。接着用这个值进比较。

类型转换部分

  每一个值都有与之关联的类型,此类型定义分配给该值的空间大小、它能够具备的可能值的范围以及它能够提供的成员等特性。许多值能够表示为多种类型。 例如,值 4 能够表示为整数或浮点值。类型转换能够建立一个等同于旧类型值的新类型值,但却没必要保留原始对象的恒等值(或精确值)。

  .NET Framework 提供了多个功能来支持类型转换。包括:

  Implicit 运算符,该运算符定义类型之间可用的扩大转换(隐式转换)。

  Explicit 运算符,该运算符定义类型之间可用的收缩转换(显示转换)。

  IConvertible 接口,该接口定义到 .NET Framework 每一个基数据类型的转换。

  Convert ,该类提供了一组方法来实现 IConvertible 接口中的方法。

  TypeConverter ,该类是一个基类,能够扩展该类以支持指定的类型到任何其余类型的转换。具体:参见此处

  上面是.Net Framework中的类型转换,下面直说点C#的。

  因为 C# 是在编译时静态类型化的,所以变量在声明后就没法再次声明,或者没法用于存储其余类型的值,除非该类型能够转换为变量的类型。 例如,不存在从整数到任意字符串的转换。 所以,将 i 声明为整数后,就没法将字符串“Hello”赋予它。

  类型转换分

  隐式转换:因为该转换是一种安全类型的转换,不会致使数据丢失,所以不须要任何特殊的语法。例如,从较小整数类型较大整数类型的转换以及从派生类到基类的转换都是这样的转换。

  显式转换(强制转换):显式转换须要强制转换运算符。在转换中可能丢失信息时或在出于其余缘由转换可能不成功时,必须进行强制转换。典型的例子包括从数值到精度较低或范围较小的类型的转换从基类实例到派生类的转换

  用户定义的转换:能够定义一些特殊的方法来执行用户定义的转换,从而使不具备基类–派生类关系的自定义类型之间能够显式和隐式转换。

  使用帮助程序类的转换:若要在不兼容的类型之间进行转换,例如在整数与 System.DateTime 对象之间转换,或者在十六进制字符串与字节数组之间转换,则可使用 System.BitConverter 类、System.Convert 类和内置数值类型的 Parse 方法,例如 Int32.Parse。

  使用as和is运算符

  因为对象是多态的,所以基类类型的变量能够保存派生类型。 若要访问派生类型的方法,须要将值强制转换回该派生类型。 不过,在这些状况下,若是只尝试进行简单的强制转换,会致使引起 InvalidCastException 的风险。 这就是 C# 提供 is 和 as 运算符的缘由。 您可使用这两个运算符来测试强制转换是否会成功,而没有引起异常的风险。 一般,as 运算符更高效一些,由于若是能够成功进行强制转换,它会实际返回强制转换值。 而 is 运算符只返回一个布尔值。 所以,若是只想肯定对象的类型,而无需对它进行实际强制转换,则可使用 is 运算符。

  Convert

  简单的说就是将一个基本数据类型转换为另外一个基本数据类型。

  Convert 类提供了一种与语言无关的方式来执行基类型之间的转换,而且该类可用于面向公共语言运行时的全部语言。 它为扩大转换和收缩转换提供了一组完整的方法,而且会对不支持的转换(例如 DateTime 值到整数值的转换)引起 InvalidCastException。 收缩转换是在已检查的上下文中执行的,若是转换失败,将引起 OverflowException。

  除了支持到每一个基类型的转换外,Convert 类还可用于将一个自定义类型转换为一个或多个预约义类型。此转换是经过 Convert.ChangeType(Object, Type, IFormatProvider) 方法执行的,而此方法包装了对 value 参数的 IConvertible.ToType 方法的调用。 这意味着 value 参数所表示的对象必须提供 IConvertible 接口的实现。请参见此处

转换运算符

  C# 容许程序员在类或结构上声明转换,以便类或结构与其余类或结构或者基本类型进行相互转换。 转换的定义方法相似于运算符,并根据它们所转换到的类型命名。 要转换的参数类型或转换结果的类型必须是(不能二者同时都是)包含类型。

转换运算符具备如下特色:

  • 声明为 implicit 的转换在须要时自动进行。
  • 声明为 explicit 的转换须要调用强制转换。
  • 全部转换都必须声明为 static

  如何使用请参见此处

经验小结:

  只有在内存存储上存在交集的类型之间才能进行隐式转换。

  显示转换尽可能使用as。

  将任意类型转换成字符串:ToString(),(object继承下来的,通常都有,固然也能够重写)。

  将字符串转换成“数值类型”(int、float、double),可以使用Type.Parse(Type type)、int.TryParse(string str,out int n)【常用】。

  Convert考虑数据意义的转换。Convert转换是从新加工、改造的过程,也就是重新组织数据结构。Convert能够把object类型转换为其余类型。Convert能够把任意类型转换为任意类型。

自娱自乐

  Type.TryParse(Type type)有什么优势呢?

  答:Parse()转换失败会抛异常,TryParse()转换失败不报异常。例如:int  result;bool p=int.TryPase(result,out result) 转换失败的话是0。

  为何说as比is转换比较高效呢?

  例如:if(p is Student){Student stu=(Student)p;},CLR会进行两次类型检查if(检查一次){//再检查一次},而as是直接转换,若是转换失败返回null而不会报异常。Is也能够用GetType()得到类型,而后转换。GetType()不容许重写。

值类型和引用类型部分

值类型

若是数据类型在它本身的内存分配中存储数据,则该数据类型就是“值类型”。 值类型包括:

  • 全部数字数据类型
  • Boolean 、Char 和 Date
  • 全部结构,即便其成员是引用类型
  • 枚举,由于其基础类型老是 SByte、Short、Integer、Long、Byte、UShort、UInteger 或 ULong

  每一个结构是值类型,即便它包含引用类型成员。

  能够经过使用保留关键字(例如 Decimal)声明值类型。 也可使用 New 关键字初始化值类型。 这对于值类型有一个带参数的构造函数的状况尤其有用。

引用类型

  “引用类型”包含指向存储数据的其余内存位置的指针。 引用类型包括:

  • String
  • 全部数组,即便其元素是值类型
  • 类类型,如 Form
  • 委托

  类是一种“引用类型”。 所以,诸如 Object 和 String 之类的引用类型都受 .NET Framework 类支持。 注意,每一个数组都是一种引用类型,即便其成员是值类型。

  因为每种引用类型表示基础 .NET framework 类,在初始化时,则必须使用 New 运算符关键字,如:Dim totals() As Single = New Single(8) {};

非类型的元素

如下编程元素未限定为类型,由于您没法将它们中的任何一个指定为声明元素的数据类型:

  • 命名空间
  • 模块
  • 事件
  • 属性和过程
  • 变量、常数和字段

使用对象数据类型

  能够将引用类型或值类型指派给 Object 数据类型的变量。 Object 变量老是存储指向数据的指针,从不存储数据自己。 然而,若是为 Object 变量指派值类型,该变量的行为将如同存储本身的数据同样。可参考该地址

性能问题

  数据传入方法做为值类型参数,即在堆栈上建立了每一个参数的副本。很明显,若是相关参数是大型数据类型(例如,包含不少元素的用户定义结构),或屡次执行方法,均可能影响性能。在这些状况下,使用 ref 关键字向类型传递一个引用可能更为可取。out 关键字相似于 ref 关键字,但它会告知编译器该方法必须为参数赋值,不然将产生编译错误。

例子

  值类型的内容存储在堆栈上分配的内存中。例如 int x=42,值 42 存储在称为“栈”的内存区域中。

  定义变量的方法在结束执行或使变量 x 超出使用范围时,其值则从栈中丢弃。使用栈效率较高,但值类型的生命周期有限,不适合在不一样类之间共享数据。

  引用类型(例如,类或数组的实例)在另外一个称为“堆”的内存区域中分配。

  例如int[] numbers = new int[10],构成数组的 10 个整数所需的空间是在堆上分配的。在方法完成时,并不将此内存归还给堆;仅当 C# 的垃圾回收系统肯定再也不须要该内存时,才进行回收。声明引用类型须要更多系统开销,但它们的优势是能够从其余类进行访问。

经验小结

  一个变量是使用基本的内置数据类型之一或用户定义的结构进行声明的,则该变量为值类型。但 string 数据类型除外,它是引用类型。

  全部引用类型都继承Object。全部的值类型都继承自System.ValueType[System.ValueType]又继承自Object。

  值类型和引用类型都用到了栈,值类型的数据存储在栈上。引用类型的引用地址是存在栈上的,数据是存在堆中。

  引用类型的变量赋值只复制对象的引用。值类型变量赋值会拷贝一个副本。

  Out和Ref

  ref 仅仅是一个地址,引用传递,把值传递强制改成引用传递。

  1.在方法中必须为out参数赋值,out 让函数能够输出多个值。

  2.out参数的变量在传递以前不须要赋值,即便赋值了也不能在方法中使用。

  ref 参数在传递以前必须赋值。在方法中能够不为ref参数赋值,也能够直接使用。

  区别是:ref应用场景内部对外部的值进行改变,out则是内部为外部变量赋值,out通常在函数有多个返回值的场所。当一个方法同时要返回多个值的时候,能够考虑使用out参数。

  ref主要是把一个值,带进去再带出来。out主要是带出来。

异常部分

  在运行时发生的错误才叫异常。语法错误,编译器能够提示,逻辑错误,编译没错,结果不正确,会使程序崩溃。程序崩溃了也要执行finally。

  catch{}//能够捕获全部的异常,catcha(Exception ex){} 得到异常的详细详细对异常进行分类,

  内部异常直接抛,最外层调用者进行处理。注:throw;只能在 catch中写。try中有return,finally也照样执行,但finally中不能写return语句。

  发生异常后,try块中,异常代码后的代码不会执行。finally块中的代码,不管是否发生异常都会执行。

  异常对象:Exception ex。此类封装了异常发生时的一些信息。

  Exception 类主要属性:Message、StrackTrace、InnerException

  扔出本身的异经常使用throw,抓住:catch。

  注意:经过逻辑判断(if-else)减小异常发生的可能性。在多级方法嵌套调用的时候,若是发生了异常,则会终止全部相关方法的调用,而且释放相关的资源。

传参部分

  在封装类时常常封装方法或构造函数等,封装时常常须要传递参数。而传参是分值传递和引用传递。传参的时候,值传递中传递的是栈中保存的数据,而引用传递则是传递自己的地址(栈和堆各有一个地址)。

  值传递:传递的是栈中的内容,例如:值类型或者引用类型做为参数传递。

  引用类型:需借助ref和out进行传递。

  引用类型的变量(字符了,例如 String str=null中,str就是个变量)是在栈上存储,堆中开辟地址存放数据。传参时,引用对象不只传参的是地址引用,同时也是一个新的对象。例如:在类中的方法传递的是类时,此时传递进方法体中的参数是一个新的对象。例如:void method(class name){}。name的对象是一个新的对象,只是这个对象指向传递进来的对象的地址,也不全是个占位符。当在这个方法体中从新赋予新的对象时(name=new class(); ),也就从新指向新的地址了(栈上的地址不变,指向堆上的地址发生变化),这时修改这个值时,原来的对象(方法中传进来的)不会改变。

  小结:我感受有点绕,不过我会绕出来的,写点方便看懂的。简单记住就是:值传递和引用传递均可传递引用类型和值类型的值,只是值传递是栈中对象的拷贝(拷贝,赝品,可是一个新的),引用传递是栈中地址,就是同一对象了。

不过传递参数尽可能用接口>抽象类>父类。

补充一点:params能够为可变参数直接传递一个对应类型的数组。

垃圾回收

  .NET Framework 的垃圾回收器管理应用程序的内存分配和释放。 每当您建立新对象时,公共语言运行时都会从托管堆为该对象分配内存。只要托管堆中有地址空间可用,运行时就会继续为新对象分配空间。 可是,内存不是无限大的。 最终,垃圾回收器必须执行回收以释放一些内存。 垃圾回收器优化引擎根据正在进行的分配状况肯定执行回收的最佳时间。 当垃圾回收器执行回收时,它检查托管堆中再也不被应用程序使用的对象并执行必要的操做来回收它们占用的内存。

  垃圾回收的基础、垃圾回收的性能、被动回收、滞后时间模式、针对共享Web承载优化、垃圾回收通知、应用程序域资源监控、弱引用。请参考此处

小结:

  时间问题,垃圾回收就偷个懒了,O(∩_∩)O~。

  垃圾回收的目的:提升内存利用率。

  垃圾回收器,只回收托管堆中的内存资源。不回收其余资源(数据库链接、文件句柄、网络端口等)

  什么样的对象才会被回收?

  没有变量引用的对象。没有变量引用的对象,表示能够被回收(null)。

  什么时间回收?

  不肯定,当程序须要新内存的时候开始执行回收。

  GC.Collect();//手动调用垃圾回收器。不建议使用,垃圾回收时会暂停一下(很是短暂)让程序区自动调用GC。

  垃圾回收器中"代"的概念:

  共3代:第0代、第1代、第2代。各代的回收频率:第0代最高,其次第1代,再次第2代。也就是说越老的对象生存概率越大。

  除了内存资源外的其余资源怎么办? Dispose()

  弱引用:WeakReference,对于建立比较耗时的对象可使用弱引用。

  弱引用例子:

  person p=new person();

  p.Age=13;

  WeakReference wk=WeakReference(p);//弱引用

  p=null;//当执行完这句话的时候,垃圾回收能够去回收p对象。一般弱引用还能够访问到p。

  object pnew=wk.Target;

  //垃圾回收不知道何时进行,因此存在漏洞。通常不用isLive。

  if(pnew != null)

  {Console.WriteLine(((Person)pnew).Age);}

  else{Console.WriteLine("对象已经被回收!")};

  //弱引用在大括号内有效。

  弱引用小结:

  弱引用通常存在那些建立是比较耗时的对象。例如打开数据库取出比较多的数据时,只要在垃圾回收以前,弱引用对该对象仍是有效地。弱引用就至关于存一个源对象的索引,只要源对象没被垃圾回收进行回收,弱引用就能够引用。

相关文章
相关标签/搜索