一、类
1.构造函数永远不会有返回值,名字总是和需要构造的类的名字相同。
2.默认的构造函数作用:把新对象分配到内存中;确保所有字段数据(类实体的“状态”)都设置为正确的默认值。
3.一旦定义了自定义构造函数,默认构造函数就被自动从类中移除。如果希望使用默认构造函数和自定义构造函数创建类型实例,就必须显示重新定义默认构造函数,大多数情况下,
类的默认构造函数的实现故意为空,因为我们需要的只是创建具有默认值的对象的能力。
4.this关键字:解决当传入参数的名字和类型数据字段的名字相同时产生的作用域歧义。静态成员中使用this关键字会产生编译错误,因为静态成员在类(而不是对象)级别产生作用,
因此在类级别没有当前对象(也就没有this)。
另一种用法:使用一项名为构造函数链的技术来设计类,串联构造函数。好处:类定义就会更容易维护、更简明,真正的工作都交给了一个构造函数(具有大多数参数)。
5.static关键字,.net框架有哪些静态类?(console,Math,Environment,GC)
静态成员只能操作静态数据或调用类的静态方法。如果使用非静态类数据或调用非静态方法,就会收到编译时错误。
静态字段数据:分配一次并且在所有类对象之间共享。非静态字段数据(实例数据),类型的每一个对象都会维护字段的独立副本。
静态构造函数:若在实例级别的构造函数中赋值给静态数据成员,每次新建对象,类级别的数据(静态字段数据)都将被重置。静态构造函数是特殊的构造函数,非常适用于初始化在
编译时未知的静态数据的值。
(1)一个类只可以定义一个静态构造数据,即,静态构造函数不能被重载。
(2)静态构造函数不允许有访问修饰符并且不能接受任何参数。
(3)无论创建了多少个类型的对象,静态构造函数只执行一次。
(4)运行库创建类实例或者调用者首次访问静态成员之前,运行库会调用静态构造函数。
(5)静态构造函数的执行优先于任何实例级别的构造函数。
静态类:直接在类级别应用static关键字,不能使用new关键字来创建,并且只能包含使用static关键字标记的成员或字段。(一般称为工具类)
除了使用静态类外,防止类被创建的两个方式:使用private关键字将默认构造函数重新定义为私用的或者使用abstract将类标记为抽象类型。(类的部分功能不能被使用)
6.const与readonly
const:定义常量字段,是隐式静态的,只能通过类级别访问(类.常量字段名:MyMath.PI)。定义常量时必须为常量指定初始值(编译时必须知道常量的值)。
readonly:只读字段的值可以在运行时决定,因此在构造函数作用域中进行赋值是合法的(其他地方不行),类对象级别访问。
static readonly:静态只读字段,从类级别访问。若在编译时已经知道静态只读字段的值,赋值方式同const;若只能在运行时才知道静态只读字段的值,则必须通过静态构造函数。
共同点:在赋值不能改变。
二、封装(encapsulation):将类的实现细节隐藏起来。
1.访问修饰符
public→类或类成员→公共的项没有限制
private→类成员或嵌套类型→私有项只能由定义它们的类进行访问
protected→类成员或嵌套类型→受保护项可以由他们的类及其子类使用,但外部类无法通过C#的点操作符访问
internal→类或类成员→内部项只能在当前程序集中访问。
protected internal→类成员或嵌套类型→项在定义它们的程序集、类以及派生类中可用。
嵌套类型:枚举,类,接口,结构或委托
(1)通过嵌套类型可以完全控制内部类型的访问级别,也就是可以声明为私有的
(2)由于嵌套类型是包含类的成员,所以它可以访问包含类的私有成员
(3)通常,嵌套类型只用做外部类的辅助方法,而不是为外部世界所准备的
2.默认情况下,类是internal,类成员是private
三、继承(inheritance):基于已有类定义来创建新类的定义,可以促进代码重用。
1.子类可以继承基类的核心功能,并扩展类的行为。
2.is-a关系又称为经典继承,使用冒号操作符。
3.has-a:包含/委托模型(聚合),不是用来建立基类/子类关系的,它意味着,一个类可以定义另一个类的成员变量。(一辆汽车有一个收音机)
4.虽然构造函数一般定义为公共的,但派生类从来没有继承父类的构造函数。
5.继承保护了封装。
6.C#要求一个类只能有一个直接基类,不支持多重继承,但可以实现多个接口。
7.sealed关键字用于阻止类的继承(String类)。不可以从结构继承结构,从类继承结构或者从结构继承类。
8.一般基类的默认构造函数会在派生类构造函数执行之前被自动调用。
9.只要子类想访问父类定义的公共或受保护的成员,我们就可以使用base关键字。
10.protected字段数据会打破封装。
11.总结:使用经典继承,包含和嵌套类型构建相关类型层次关系
四、多态(polymorphism):基类为所有的派生类定义一个成员集合(多态接口:由任意个虚拟virtural或抽象abstract成员组成)。
1.虚拟成员是基类中已经有了默认实现的成员,它可能被派生类重写;抽象方法是基类中不提供默认显示的成员,但它提供签名,当一个派生类自定义了抽象方法的基类时,
抽象方法必须被派生类所重写。
2.如果基类希望定义可以(不是必需的)由子类重写的方法,就必须用virtural关键字标志方法,该方法称为虚方法。如果子类希望改变虚方法的实现细节,就必须使用override关键字。
3.可以密封基类的虚方法,防止派生类的派生类重写。public override sealed void GiveBonus(decimal a);
4.当你希望定义没有提供默认实现而必须在每个派生类中实现的成员时,可以使用抽象成员。这样做就强制了每一个后代具有多态接口。
5.抽象方法只可以定义在抽象类中。抽象方法没有提供默认实现,只是一个方法签名。
6.尽管不能直接创建抽象基类的实例,我们完全可以使用抽象基类变量来保存指向任何子类的引用。
7.成员投影:如果派生类定义的成员和定义在基类中的成员一致,派生类就投影了父类的版本。
解决方法:(1)把基类的方法定义为虚方法或抽象方法,派生类通过override重写。(2)为派生类方法添加new关键字,隐藏基类的版本。
五、类类型强制转换操作
规则
(1)如果两个类通过is-a关系关联,在基类引用中保存派生类型总是安全的。这叫隐式转换,因为它基于继承的规则。
(2)使用C#强制转换操作符进行显示的向下转换。(A)b. 显示强制转换在运行时而不是编译时进行运算。as关键字。
is关键字:检测两个项是否兼容。
六、System.Object
// 摘要: // 支持 .NET Framework 类层次结构中的所有类,并为派生类提供低级别服务。 这是 .NET Framework 中所有类的最终基类;它是类型层次结构的根。 [Serializable] [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class Object { // 摘要: // 初始化 System.Object 类的新实例。 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] public Object(); // 摘要: // 确定指定的 System.Object 是否等于当前的 System.Object。 // // 参数: // obj: // 要与当前对象进行比较的对象。 // // 返回结果: // 如果指定的对象等于当前对象,则为 true;否则为 false。 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public virtual bool Equals(object obj); // // 摘要: // 确定指定的对象实例是否被视为相等。 // // 参数: // objA: // 要比较的第一个对象。 // // objB: // 要比较的第二个对象。 // // 返回结果: // 如果认为对象相等,则为 true;否则为 false。 如果 objA 和 objB 均为 null,该方法返回 true。 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public static bool Equals(object objA, object objB); // // 摘要: // 用作特定类型的哈希函数。 // // 返回结果: // 当前 System.Object 的哈希代码。 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public virtual int GetHashCode(); // // 摘要: // 获取当前实例的 System.Type。 // // 返回结果: // 当前实例的准确运行时类型。 [SecuritySafeCritical] public Type GetType(); // // 摘要: // 创建当前 System.Object 的浅表副本。 // // 返回结果: // 当前 System.Object 的浅表副本。 [SecuritySafeCritical] protected object MemberwiseClone(); // // 摘要: // 确定指定的 System.Object 实例是否是相同的实例。 // // 参数: // objA: // 要比较的第一个对象。 // // objB: // 要比较的第二个对象。 // // 返回结果: // 如果 objA 是与 objB 相同的实例,或者如果二者都为 null,则为 true;否则为 false。 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public static bool ReferenceEquals(object objA, object objB); // // 摘要: // 返回表示当前对象的字符串。 // // 返回结果: // 表示当前对象的字符串。 public virtual string ToString(); }
1.ToString():这个方法返回对象的完全限定名(命名空间.类名)。子类重写这个方法,一般是返回属性名/值字符串(["Name":"Tom";"Age":30])。
2.重写System.Object.Equals():比较传入对象和本对象各个属性值是否相等。也可以通过以下方法:
public override bool Equals(object obj)
{
return obj.ToString() == this.ToString();
}
3.重写System.Object.GetHashCode():如果一个类重写了Equals()方法,我们还需要重写默认的GetHashCode()实现。
(1)散列码是表示对象某个状态的数字值,例如,如果创建两个字符串对象来保存Hello值,应该得到同样的散列码。
(2)默认情况下,System.Object.GetHashCode()使用对象在内存中的当前位置来产生散列值。
如果类中存在某个属性与其它对象都不相同(主键Id),则可以使用这些属性调用GetHashCode(),
public override int GetHashCode()
{
return Id.ToString().GetHashCode();
}
或
public override int GetHashCode()
{
return this.ToString().GetHashCode();
}