- 类与结构
- 类和结构都是对象的模板
- 类定义了处理和访问数据的方法,通过类的实例化进行逻辑处理
- 类与结构的区别是类是引用类型,存储在托管堆上;结构是值类型,存储在栈上的;
类使用class进行修饰,结构使用struct修饰,类的实例通过关键字new进行声明
class PhoneCustomer{ public const string DayOfSendingBill="Monday"; public int CustomerID; public void Get() { } }struct PhoneCustomerStruct{ public const string DayOfSendingBill="Monday";}PhoneCustomer pCustomer=new PhoneCustomer();pCustomer.Get();
- 类
- 数据成员
- 类中的数据和函数都是类的成员,类可以嵌套即类中包含类.
- 类的成员使用访问修饰符修饰。访问修饰符有public、protected、internal protected、private、internal
- public公开的可访问的
- protected派生类可以访问
- internal protected同一命名空间下派生类可以访问
- private私有的,本身类中才可以访问
- internal同一命名空间下可以访问
- 类的数据成员包含类的数据——字段、常量和事件的成员,成员可以是静态数据,静态成员使用static关键字进行声明,常量是经常使用的变量,使用const修饰
- 函数成员
- 方法
- 函数成员包含方法、类或结构、索引器、运算符、构造函数、析构函数
- 方法声明
- 方法由访问修饰符、返回值类型、方法名、输入参数列表和方法体组成
- 参数列表由参数类型(数据类型)和参数名(方法体引用名)组成。方法由返回值,则通过return关键字返回结果,如果没有返回值指定返回类型为void
-
public string Add(string name){ return "我的名字是:"+name; }public void Add(string name){ Console.Write("我的名字是:"+name); }
- 方法调用
- 方法通过对象的实例进行调用,使用static修饰的方法使用类名进行调用.
- 参数传递
- 参数通过引用或值传递给方法
- 在变量通过引用传递给方法时,被调用的方法得到的是这个变量即内存中变量的指针。
- 所以在方法内部对变量进行的修改在方法退出后仍旧有效。
- 如果变量通过值传递给方法,被调用的方法得到的变量是一个复制副本,方法退出后,对变量的修改会丢失。
- 对于复杂的数据类型,按引用传递的效率更高,因为值传递时,需要复杂大量数据。
- 参数通过引用或值传递给方法
- ref参数
- 使用ref关键字传递方法参数时,必须给传递给方法的参数进行初始化。
- 使用ref关键字传递值变量时,迫使值传递通过引用传送给方法。
- out参数
- 使用out关键字传递方法参数,必须给传递给方法的参数进行初始化。
- out参数一般用来输出多个返回值。因为return只能返回一个值。
- 对于现在的C#而言,out参数用法已经很少了,可以使用tuple类进行使用。
- 命名参数
- 调用方法时,指定方法参数名和参数值,这样可以不按照顺序去传值。坏处是,调用方法的代码太长
- 可选参数
- 可选参数一般作为方法的最后一个参数值,作为可选参数。作为可选参数,在定义时必须给予默认值,它和重载方法是有区别的,
- 重载方法是不同方法,而可选参数弥补了这一缺失,给定一个方法可选参数指定。
-
public deciimal Add(int num,decimal price=12){ return num*price;}调用Add(5)或者Add(5,10)
- 方法重载
- 不同方法,方法名相同方法参数不同。
- 方法参数不同表现在参数类型不同、参数数量不同
- 属性
- 属性表现为一个方法或一对方法,和字段不同的是,它有get和set访问器;属性允许给它定义一个私有字段,get和set访问器对私有字段进行了读写操作。
- 私有字段的命名可以以“_”开头,但是必须小写开头。
- get访问器提供了返回属性声明的类型值;
- set访问器需要指定值,编译器给定假设值value.
- 只读只写属性
- 只读属性是因为只设置了get访问器,没有set访问器,使属性不可修改。一般情况有set访问器不是必须定义的。
- 属性的访问修饰符
- C#允许给属性get和set访问器定义修饰符,属性因此可以具备公有的get访器和受保护的set访器
- 自动属性
- 即直接声明属性,不用定义get和set访器的行为方式。也不用定义私有字段。编译器会自动创建。但是自动属性不能验证值的有效性。
- 构造函数
- 类的实例化函数,构造函数就是声明一个方法名与类名相同,但是没有返回类型,构造函数还允许重载。在默认情况下编译器提供了默认的构造函数。构造函数是没有参数的。但是一旦定义了有参数的构造函数,编译器则不提供默认的构造函数。构造函数参数名允许和类的属性名相同,但是调用时允许使用关键字this进行指定。
- 静态构造函数
- 静态构造函数顾名思义,函数名和类名相同,且必须是静态static的,一个类只能有一个静态构造函数。且不能有访问修饰符。因为这个函数仅仅用于初始化,不能调用和外部传参。因此函数也不存在参数。函数在类调用之前,.NET运行库就讲其进行初始化。同时因为是静态的,所以函数体内不能使用实例成员属性。只能使用静态成员。
- 构造函数中调用构造函数
- 一个类中可以有多个构造函数,使用this关键字调用类中其他的构造函数,如果需要调用基类的构造函数可以使用base关键字
- 只读字段
- 使用readonly进行修饰。与常量不同的是,常量需要给定初始值,而readonly则不需要在定义时指定。一旦指定值之后,不能对值进行修改。这会在运行时进行判断。
- 方法
- 匿名类型
- var关键字,用于表示隐式类型的变量,var与new关键字同时使用,才能创建匿名类型
- 匿名类型是一个继承Object类,没有名称的类型,通过初始化器判断类型的属性,因此该类型必须初始化定义类型行为和属性。
- 匿名类型中的属性允许是匿名类型,一般情况下匿名类型实例化后的对象属性是只读的。但是如果匿名类型定义的属性,其属性值是对象引用类型。则是可以修改的。
-
属性嵌套var captain=new { FirstName = new { name="admin", age=12 }}引用类型可写属性var doctor=new { Items=new List (), Captain=captain}
另外提一点,匿名类型实例对象的ToString()方法可以将匿名类型转换为json对象字符串。至于匿名方法则和委托离不开,后续会说明。
- 结构
- 结构是值类型,数据接口比类小。
- 结构是不能被继承的,并且编译器会给定默认的无参构造方法,我们不能定义无参构造方法。
- 使用结构,可以指定字段在内存中的布局
- 类与结构的区别
- 结构是值类型,不用new实例,只要定义就能使用属性进行设置属性值。但是不能使用没有初始化的属性作为值传递。否则会发生异常。
- 正常的代码:
异常的代码
结构在分配内存时速度快,不需要等待垃圾回收,只是作为方法参数传递时,因为是值类型,传递的是结构的复制品,方法体内修改后会丢失。因此需要加上ref关键字进行传递。
- 结构和继承
- 结构不能被继承,但是结构继承了Object类,因此拥有了Object类的行为
- 结构的构造函数,因为结构的构造函数定义了默认构造函数,所以结构中定义的字段不能直接赋值。因为默认构造函数会给这些字段赋初始值,如果是数值则默认赋值0,如果是对象默认赋null
- 数据成员
-
- 弱引用
在应用程序代码内实例化一个类或结构时,只要有代码引用,就会形成强引用。这意味着垃圾清理器在其类或结构的使用范围中有引用它的地方都不会清理内存。一般而言是好事,但是对于大数据量且不经常访问的对象进行强引用时,占用内存巨大。此时可以使用弱引用。
弱引用:由WeakReference类创建的。该对象可能任何时刻被回收。所以引用该对象时,需要确认对象数据是否存在。
创建对象时,实例化WeakReference类对象,作为初始化构造器,将被创建的对象作为WeakReference类构造方法参数进行初始化。在调用时使用WeakReference实例对象的IsAlive属性判断是否存在数据。存在则说明可以调用该对象。通过WeakReference实例对象的Target属性获得该对象。但是因为Target属性的数据类型是Object类型,因此在获取对象时需要使用as关键字进行强制转换。
-
- 部分类
partial关键字可以将一个类、结构、方法、接口放在多个文件中。一般情况下,一个类会有很多方法和属性,因为某些属性和方法需要分离开来,比如类的一部分成员是设计部分。一部分成员是业务逻辑部分,这时候需要拆分为2个文件方便维护和开发。还有时候因为类的一部分成员是供其他功能调用的。一部分成员作为单个功能使用的。也可以拆分出来。编译时,编译器会将partial标识的多个文件同属于一个类里面的成员、特性、XML描述、接口、泛型进行合并在一起。
例2
不过需要注意的是,如果声明类中使用了下面的关键字,那么多个文件的同一个分部类的这些关键字标识要保持一致。
-
- 静态类
静态类在类的声明class前加上static修饰。当然类都是静态的了,那么属性和方法都必须是静态的,调用静态成员时直接使用类名进行调用即可。因为是静态的.NET运行库在调用时便已经进行了初始化。所以通过类型调用即可。
-
- Object类
所有.NET类都派生自Object类,对于结构,这个派生是间接的,结构是派生自ValueType值类型,值类型派生自Object类。
Object类公开方法
ToString方法获取对象的字符串,或者进行格式化,或者转为json格式,允许被重写。
GetHashCode方法,如果对象放在名为映射(散列表或字典)的数据结构中,就可以使用该方法。当类作为字典的一个键时,需要重写该方法。
Equals方法,比较对象是否相等的方法,在.NET FrameWork中比较相等是有相当复杂的模式的。
Finalize方法,在Object类中没有什么行为。在引用对象作为垃圾被回收以清理资源时调用它。如果对象有对未托管资源的引用。可以重写该方法。
GetType方法,返回类的实例,这个对象实例提供对象成员所属类的很多信息
MemberwiseClone方法,只复制对象,返回对副本的一个引用。得到的副本是一个浅表复制,即它复制了类中所以值类型,如果类包含内嵌引用,只复制引用,不复制对象。
-
- 扩展方法
扩展方法它表现在静态类的静态方法中,如果一个封装的代码类不能进行修改。但是需要给它添加方法则需要使用到扩展方法。
扩展方法在静态方法中,方法参数的第一个参数使用this修饰,第一个参数类型即被扩展的类类型,在方法体内,通过第一个参数类型的实例对象访问对象的成员。不过只能访问该对象的所以公共成员。如果扩展方法的参数与已有类型的方法冲突,可能会造成方法被替换的问题。
扩展方法在调用时,直接是被扩展类的实例调用此扩展方法即可。