一.
“类”是一种构造,通过使用该构造,您可以将其他类型的变量、方法和事件组合在一起,从而创建自己的自定义类型。如果类没有声明为静态类,客户端代码就可以创建赋给变量的“对象”或“实例”,从而使用该类。在对变量的所有引用都超出范围之前,该变
量始终保持在内存中。所有引用都超出范围时,CLR 将标记该变量以供垃圾回收。如果类声明为静态类,则内存中只存在一个副本,并且客户端代码只能通过该类自身而不是“实例变量”访问该类。
与结构不同,类支持“继承”,而继承是面向对象编程的基础特性。
类使用 class 关键字进行声明。
class 关键字前面是访问级别。如果使用 public,任何人都可以基于该类创建对象。
对象是基于类的具体实体,有时称为类的实例。通过使用 new 关键字(后跟对象将基于的类的名称)可以创建对象。
由于基于类的对象是按引用来引用的,因此类称为引用类型。
this关键字提供对当前实例的访问。
二.
类继承
继承是通过使用“派生”来实现的,而派生意味着类是使用“基类”声明的,它的数据和行为从基类继承。通过在派生的类名后面追加冒号和基类名称,可以指定基类,例如,
public class Manager : Employee
{
// Employee fields, properties, methods and events are inherited
// New Manager fields, properties, methods and events go here...
}
当类声明基类时,它继承基类除构造函数以外的所有成员。
与 C++ 不同,C# 中的类只能直接从一个基类继承。但是,因为基类自身也可能继承自另一个类,所以类可以间接继承多个基类。而且,一个类可以直接实现一个以上的接口。
类可以声明为抽象类。抽象类包含具有签名定义但没有实现的抽象方法。抽象类不能进行实例化。只能通过实现抽象方法的派生类使用抽象类。相比之下,密封类不允许其他类从其派生。
类定义可在不同的源文件之间进行拆分,这叫分部类。
三.
一个类对象中,主要包括字段、属性和方法。不过除此之外,在类类型中还可以定义嵌套类,也可以定义一个常量。
常量:
const PI = 3.1415926;
square = PI * radius;
类的字段其实也是变量。字段仍然可以利用public,internal,protected,private来修饰它。不过,建议如非特殊情况,不要将字段修饰为public。因为,根据”对象封装”的原则,应尽量避免将一个类型的字段以公有方式提供给外部。毕竟,对于字段而言,对象对
它的控制非常弱,一旦公开在外,则调用者可以比较容易的对其进行操作,尤其是写操作,从而可能会导致错误。例如,如果为User类增加一个age(年龄)字段,假如将其定义为public字段,
public int Age;
那么调用者可能会将Age的值设为负数:
user.Age = -5;
如果字段不能设置为public,那么调用者又如何访问它们呢?使用C#类中的property(属性)。
“属性”,它利用一种被称为“get/set访问器”分别控制对字段的读写操作,并暴露一个属性值,如Age属性:
private int m_age;
public int Age
{
get {return m_age;}
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException("Age must be greater than or equal to 0");
}
m_age = value;
}
}
首先,定义一个私有字段m_age,然后再定义一个公共属性Age。在该属性中,get返回私有字段的值m_age,而在set中,首先会判断value的值,如果小于0,那么这个值是非法的,就将抛出一个异常,停止往下执行,并告诉你对Age值的设置错误了。当然,也可以为value
值设置更严格的要求,例如不允许value大于150。
value是什么?它是C#中提供的关键字,代表的是赋给该属性的真正的值,例如:
user.Age = 30;
此时是对Age属性赋值,.Net会执行set访问器,而value值就是30。然后判断30是否小于0。符合条件,不会抛出异常,继续执行,将value值30赋给字段m_age。
为什么要赋给m_age呢?看看get访问器,它其实就是一个读操作,返回的值是什么?就是字段m_age,如下所示:
user.Age = 30; //set操作,将字段m_age设置为30;
Console.WriteLine(“User’s Age is {0}.”, user.Age); //get操作,将m_age的值取出;
此时就会在控制台下显示:
User’s Age is 30.
此外,对于一些特殊的要求,我们在将字段封装为属性时,可以只设置它的get访问器或者set访问器,这样这个属性就是只读属性,或者只写属性了。这样更有利于对象的封装。
毕竟对于公共字段而言,我们最能可以控制它为只读(设置为readonly),却无法设置为只写。
从上可以看到,实际上属性就是对字段进行一次封装。
在C# 2.0中,除了可以对整个属性设置public等访问修饰符外,对内部的get/set访问器同样可以设置访问修饰符,当然它要受到一定的限制。
访问器和属性的访问修饰符冲突问题。
1、如果整个属性被设置为public,则其访问器没有限制;
2、如果整个属性被设置为protected internal,则访问器的访问修饰仅能设置为internal,protected或者private中的一种;
3、如果整个属性被设置为internal或者protected,那么访问器的访问修饰只能是private。
四
1 使用this进行串联构造函数调用
使用一项名为构造函数链的技术来设计类。当类定义个了多个构造函数时,这个设计模式就会很有用。
由于构造函数通常会检验传入的参数来强制各种业务规则,所以在类的构造函数集合中经常会找到冗余的验证逻辑。
串联构造函数方案:让一个接受最多参数个数的构造函数做“主构造函数”,并实现必须的验证逻辑。其余的构造函数使用this关键字把参数转给主构造函数,并提供其他必需的参数。这样,我们只关心主构造函数的逻辑,而其他构造函数体基本是空的了。
使用this关键字串联构造函数方式可以简化编程任务,类定义更加容易维护、更更加简明。但它不是强制使用的。
串联构造函数的执行顺序:
1、调用构造函数把调用者提供的参数值转发给主构造函数,并提供其他必须的初始化参数值。
2、执行主构造函数。
3、执行调用构造函数体的逻辑。
2 自定义索引器
class CarCollection:IEnumerable{
private ArrayList arCar=new ArrayList();
public Car this[int index]
{
get{ return (Car)arCar[index];}
set{arCar.Insert(index,value);}
}
//...
}
3 static关键字
C#类(或者结构)可以使用static关键字来定义许多静态成员。这些静态成员只能从类级别而不能从对象级别上调用(调用静态成员时不需要创建实例对象)。
例如:
//错误,WriteLine是静态成员,是类级别的方法。
Console c=new Console();
c.WriteLine("Bruce Wong");
//正确!WriteLine是类级别的方法
Console.WriteLine("Bruce Wong");
静态成员只能操作静态数据或调用类的静态成员。而非静态成员可以操作实例数据与静态数据(成员),因为静态成员对类的所有实例都是可用的。
CLR把静态数据分配到内存只进行一次,改变静态数据将影响此类的所有实例。
定义静态构造函数
构造函数用于在创建类对象时设置类对象的数据值。如果使用实例级别的构造函数给静态数据赋值,你会发现每次新建类对象时静态数据的只都会被重置。所以我们要初始化静态数据最好使用静态构造函数。
静态构造函数是特殊的构造函数,它非常适用于初始化在编译时未知的静态数据的值:
一个类(结构)只能定义一个静态构造函数。
静态构造函数不允许访问修饰符并且不能接受任何参数。
无论创建多少个类实例,静态函数知执行一次。
CLR创建类实例或首次调用类静态成员前,CLR会调用静态构造函数。
静态构造函数先于实例级别的其他构造函数执行。
静态类:一个类被定义为静态的(使用static关键字修饰),就不能使用new关键字来创建类实例,静态类只能包含用static标记的静态类成员或
五 案例
1
码码:
using System;
public class Person
{
// 字段
public string name;
// 无参构造函数
public Person()
{
name = "不知道";
}
// 带一个参数构造函数
public Person(string nm)
{
name = nm;
}
// 方法
public void SetName(string newName)
{
name = newName;
}
}
class TestPerson
{
static void Main()
{
// 调用无参构造函数
Person person1 = new Person();
Console.WriteLine(person1.name);
person1.SetName("张三");
Console.WriteLine(person1.name);
// 调用带一个参数构造函数
Person person2 = new Person("李四");
Console.WriteLine(person2.name);
// 等待拿下键再关闭控制台窗口
Console.WriteLine("按任意键继续......");
Console.ReadKey();
}
}
// Output:
// unknown
// John Smith
// Sarah Jones
图图:
2
码码:
/*
* Created by SharpDevelop.
* User: Administrator
* Date: 2015/3/31
* Time: 13:04
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
namespace leidemo2
{
public class Car{
//Car的字段(状态)
private int _speed;
private string _name;
//Car操作字段的属性
public int Speed {
set {this._speed=value;}
get{return this._speed;}
}
public string Name
{
set { this._name=value;}
get{return this._name;}
}
//显式定义默认构造函数
public Car(){}
//自定义构造函数
public Car(string name,int speed)
{
this._name=name;
this._speed=speed;
}
//Car的功能(方法)
public void ShowState()
{
Console.WriteLine("车 {0} 的速度是 {1} MPH", this._name,this. _speed);
}
}
class Program
{
public static void Main(string[] args)
{
//创建Car对象。
Car car; //声明了指向尚未创建的Car对象的引用。
car=new Car("宝马",150);//通过new把有效的引用赋给对象,这引用才会指向内存有效的对象。
car.ShowState();
Console.Write("按任意键继续 . . . ");
Console.ReadKey(true);
}
}
}
图图: