类
“类”是一种构造,通过使用该构造,您可以将其他类型的变量、方法和事件组合在一起,从而创建自己的自定义类型。 类就像一个蓝图, 它定义类型的数据和行为。
如果类没有声明为静态类,客户端代码就可以创建赋给变量的“对象”或“实例”,从而使用该类。 在对变量的所有引用都超出范围之前,该变量始终保持在内存中。
所有引用都超出范围时,CLR 将标记该变量以供垃圾回收。
如果类声明为静态类,则内存中只存在一个副本,并且客户端代码只能通过该类自身而不是“实例变量”访问该类。
class 关键字前面是访问级别。 由于在该例中使用 public,因此任何人都可以基于该类创建对象。 类的名称位于 class 关键字的后面。
定义的其余部分是类的主体,用于定义行为和数据。 类的字段、属性、方法和事件统称为“类成员”。
创建类的实例后,将向程序员传递回对该对象的引用。
类的继承
继承是通过使用“派生”来实现的,而派生意味着类是使用“基类”声明的,它的数据和行为从基类继承。
通过在派生的类名后面追加冒号和基类名称,可以指定基类。当类声明基类时,它继承基类除构造函数以外的所有成员。因为基类自身也可能继承自另一个类,所以类可以间接继承多个基类。
而且,一个类可以直接实现一个以上的接口。
特别说明下私有成员被继承的问题:基类私有成员在继承类中,可以通过基类中的代码进行操作。例如:基类中定义的属性——在基类中定义一个私有成员的属性,在继承类中可以读写这个属性,而在属性中操作的是基类私有成员。在的基类没被实例化的前提下,假如私有成员没有被继承下来,那么继承类操作的属性中私有成员是哪来的呢?这时基类应当在内存中没有开辟空间,那么私有字段应当不存在,访问应当报错,可事实却是没有。
我想这应该C#设计、编译有关,但我不清楚编译时是什么情况。我的想法是,私有成员是被继承下来的,但是在继承类是无法操作的。继承类和基类中私有成员
的关系,就好比你捡到的一个有钱的保险箱(箱子是别人的,也就是好比继承的),但你不知道密码(意思是打不开它)。你能提放箱子、输入密码等(操作属性),但是却取不出钱(钱是箱子内部私有的)。箱子就在手中,钱就在箱子中,但是你打不开箱子,用不了这钱,你说这钱是不是你的?所以私有成员是事实上继承的,但相当于没有被继承(这说的继承后的成员是你可以操作的)。
分部类
类定义可在不同的源文件之间进行拆分。可以将类或结构、接口或方法的定义拆分到两个或多个源文件中。
每个源文件包含类型或方法定义的一部分,编译应用程序时将把所有部分组合起来。在以下几种情况下需要拆分类定义:
处理大型项目时,使一个类分布于多个独立文件中可以让多位程序员同时对该类进行处理。
使用自动生成的源时,无需重新创建源文件便可将代码添加到类中。 Visual Studio 在创建 Windows 窗体、Web
服务包装代码等时都使用此方法。 无需修改 Visual Studio 创建的文件,就可创建使用这些类的代码。
分部类和方法:http://msdn.microsoft.com/zh-cn/library/wa80x488.aspx
对象
由于类是引用类型,因此类对象的变量引用该对象在托管堆上的地址。
如果将同一类型的第二个对象分配给第一个对象,则两个变量都引用该地址的对象。类的实例是使用 new 运算符创建的。
结构对象
由于结构是值类型,因此结构对象的变量具有整个对象的副本。 结构的实例也可以使用 new 运算符来创建,但这不是必需的。
这就是在赋值时复制结构的一个原因。
相比之下,当对类实例对象的所有引用都超出范围时,为该类实例分配的内存将由公共语言运行时自动回收(垃圾回收)。
公共语言运行时中高度优化了托管堆上内存的分配和释放。 在大多数情况下,在堆上分配类实例与在堆栈上分配结构实例在性能开销上没有显著的差别。
注意:结构对象是值类型而类是引用类型。
对象标识与.值相等性
在比较两个对象是否相等时,首先必须明确您是想知道两个变量是否表示内存中的同一对象,还是想知道这两个对象的一个或多个字段的值是否相等。
如果您要对值进行比较,则必须考虑这两个对象是值类型(结构)的实例,还是引用类型(类、委托、数组)的实例。
若要确定两个类实例是否引用内存中的同一位置(意味着它们具有相同的标识),可使用静态 Equals 方法。
若要确定两个结构实例中的实例字段是否具有相同的值,可使用 ValueType.Equals 方法。 由于所有结构都隐式继承自
System.ValueType,因此可以直接在对象上调用该方法。
Equals 的 System.ValueType 实现使用反射,因为它必须能够确定任何结构中有哪些字段。 在创建您自己的结构时,重写 Equals
方法可以提供针对您的类型的高效求等算法。
要确定两个类实例中字段的值是否相等,您可以使用 Equals 方法或 == 运算符。
但是,只有类通过已重写或重载提供关于那种类型对象的相等含义的自定义时,才能使用它们。类也可以实现 IEquatable<T> 接口或
IEqualityComparer<T> 接口。 这两个接口都提供可用于测试值相等性的方法。
小结:用==运算符判断两个字符相等,其实里面调用的还是Equals方法,只是在相等和不相等返回的信息不同罢了。通过Equals方法判断是否是同一个对象不靠谱,所以判断两个对象是否是同一个对象用:object.ReferenceEquals();
抽象类、密封类及类成员
使用 abstract 关键字可以创建不完整且必须在派生类中实现的类和类成员。
使用 sealed 关键字可以防止继承以前标记为 virtual 的类或某些类成员。
抽象类和类成员
通过在类定义前面放置关键字 abstract,可以将类声明为抽象类。
抽象类不能实例化。 抽象类的用途是提供一个可供多个派生类共享的通用基类定义。
例如,类库可以定义一个抽象类,将其用作多个类库函数的参数,并要求使用该库的程序员通过创建派生类来提供自己的类实现。
抽象类也可以定义抽象方法。 方法是将关键字 abstract 添加到方法的返回类型的前面。
抽象方法没有实现,所以方法定义后面是分号,而不是常规的方法块。 抽象类的派生类必须实现所有抽象方法。
当抽象类从基 类继承虚方法时,抽象类可以使用抽象方法重写该虚方法。
此例子可以很好的应用于项目中
public class D
{ public virtual void DoWork(int i) { // Original
implementation.}}
public abstract class E : D
{ public abstract override void DoWork(int i);}
public class F : E
{ public override void DoWork(int i) { // New implementation.
}
}
如果将 virtual 方法声明为 abstract,则该方法对于从抽象类继承的所有类而言仍然是虚方法。
继承一个抽象方法的类不能访问该方法的原始实现。在上一个示例中,类 F 中的 DoWork 不能调用类 D 中的 DoWork。
通过这种方式,抽象类可以强制派生类为虚方法提供新的方法实现。
密封类和类成员
通过在类定义前面放置关键字 sealed,可以将类声明为密封类。
密封类不能用作基类。 因此,它也不能是抽象类。 密封类禁止派生。 由于密封类从不用作基类,所以有些运行时优化可以略微提高密封类成员的调用速度。
在对基类的虚成员进行重写的派生类上,方法、索引器、属性或事件可以将该成员声明为密封成员。 在用于以后的派生类时,这将取消成员的虚效果。
方法是在类成员声明中将 sealed 关键字置于 override 关键字的前面。
利用密封类不能被继承的特性,可以防止它人在写的类后进行继承造成混乱。例如:String类。
抽象属性
抽象属性声明不提供属性访问器的实现,它只声明该类支持属性,而将访问器实现留给派生类。
//关键代码
public abstract double Area
{
get;
}
//重写代码
public override double Area
{
get
{
return
radius * radius * System.Math.PI;
}
}
小结
类具有单根性,间接的继承多个类。类可以继承多个接口。
抽象可以包含抽象方法、属性、索引器、事件【其实这几个最后都是方法】;
抽象类必须在抽象类中;
抽象类中可以有实现成员也可以由抽象成员;
抽象类就是为了重写→多态(代码重用);
抽象类中可以有构造函数;