面向对象编程
第二章 面向对象编程
1.类的定义、方法重载
定义:
类的访问修饰符 修饰符 class 类名
{
成员访问修饰符 类的成员
}
注:
(1)类的访问修饰符:用于设定对类的访问限制,包括 public、internal 或者不写,用 internal 或者不写时代表只能在当前项目中访问类;public 则代表可以在任何项目中访问类。
(2)修饰符:修饰符是对类本身特点的描述,包括 abstract、sealed 和 static。abstract 是抽象的意思,使用它修饰符的类不能被实例化,只能继承,可以有抽象成员;sealed 修饰的类是密封类,不能被继承;static 修饰的类是静态类,不能被实例化。
(3)类名:类名用于描述类的功能,因此在定义类名时最好是具有实际意义,这样方便用户理解类中描述的内容。在同一个命名空间下类名必须是唯一的。
(4)类的成员:在类中能定义的元素,主要包括字段、属性、方法、事件等。
(5)成员访问修饰符:public为公共的,访问不受限制,可被任何其他的类访问;private为私有的,访问只限于该类的成员;protected为保护的,访问只限于该是类的成员,及该类的派生类;默认的访问修饰符为private。
重载:
在同一个类中,方法名相同,但是返回值类型不同(不是必须的)或者参数(数据类型、个数、顺序)不同(必须的)的方法。
例:class OverLoadTest
{
public void Hello()
{
}
public void Hello(string str)
{
}
public string Hello(int t)
{
}
}
在这段代码中有三个具有相同名称的方法Hello,他们彼此之间是重载的关系,注意两点:他们共同存在于一个类中,虽有同样的名字但是执行不同的操作;第一个方法和第三个方法只有返回类型不同,这在C#中是不允许的,编译器会报出“已定义一个名为"Hello"的方法”的错误。
重写:
发生在子类(派生类)上。只能重写virtual修饰的(虚方法)和abstract修饰的(抽象方法),即在子类中将父类的成员方法的名称保留,重写成员方法的实现内容,更改成员方法的存储权限,或是更改成员方法的返回值类型。特殊的,子类与父类的成员方法的返回值,方法名称、参数类型及个数完全相同,唯一不同的是方法的实现内容,这种特殊的重写方式称为重构。
注意:
1)静态方法不能被重写,可以被重载。
2)重写父类方法时,修改方法的修饰权限只能从小的范围修改到大的范围,不能降低方法的修饰权限范围!
(public > protected > default > private)
覆盖:
在子类中用 new 关键字修饰 定义的与父类中同名的方法。覆盖不会改变父类方法的功能。
1.当用子类创建父类的时候,如 C1 c3 =new C2(),重写会改变父类的功能,即调用子类的功能;而覆盖不会,仍然调用父类功能。
2.虚方法、实方法都可以被覆盖(new),抽象方法,接口 不可以。
3.抽象方法,接口,标记为virtual的方法可以被重写(override),实方法不可以。
隐藏:指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual
关键字,基类的函数将被隐藏(注意别与重载混淆)。
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual
关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
2.类的继承和多态
继承:任何一个类都可以从另外一个类继承,然后这个类就拥有它继承类的所有成员。被继承的类称为父类或基类。C#中一次只允许继承一个类,不能同时继承多个类。继承类时,需使用冒号加类名。
例: test2继承test1,也称test1派生test2
class test2:test1
{
成员函数、方法
}
注意:当一个类使用sealed修饰符时,此修饰符会阻止其他类从该类继承。
多态:使子类(派生类)的实例可以直接赋予基类的变量(这里不需要进行强制类型转换),然后直接可以通过这个变量调用子类(派生类)的方法。
在C#中可以通过多种途径实现多态性:
1)虚方法:需要使用基类实例化的对象,可将父类的方法标记为虚方法,使用关键字virtual,此方法在子类中可以重写(使用关键字override)
2)抽象类与抽象方法:如果我们不需要使用父类创建对象,它的存在只是为供子类继承。可以将父类写成抽象(关键字abstract)类,将父类的方法写成抽象方法,子类中的方法仍用关键字override重写。
3)接口实现
例: https://www.cnblogs.com/lihq-sharefield/p/6822226.html
3.泛型编程:即通过参数化类型来实现在同一份代码上操作多种数据类型。
例: public class test<T>
{
}
interface 接口名 <T>
{
接口体;
}
使用where施加约束,列举6种约束类型:
T:struct 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型。
T:class 类型参数必须是引用类型,包括任何类、接口、委托或数组类型。
T:new() 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。
T:<基类名> 类型参数必须是指定的基类或派生自指定的基类。
T:<接口名称> 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。
T:U 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束.
https://www.cnblogs.com/arxive/p/6179972.html
4.扩展方法
扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。它们的第一个参数指定该方法作用于哪个类型,并且该参数以 this 修饰符为前缀。 扩展方法不能破坏面向对象封装的概念,所以只能是访问所扩展类的public成员。
扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。
扩展方法的目的就是为一个现有类型添加一个方法,现有类型既可以是int,string等数据类型,也可以是自定义的数据类型。
例: static class Program
{
//扩展方法 实现两个字符串的连接
static void Main(string[] args)
{
string string1 = "123";
string string3 = string1.StrAddStr("456");
Console.WriteLine(string3);
Console.ReadKey();
}
public static string StrAddStr(this string str1, string str2)
{
return str1 + str2;
}
}
扩展方法的定义规则:
(1)扩展方法必须在一个非嵌套、非泛型的静态类中定义;
(2)它至少要有一个参数;
(3)第一个参数必须加上this关键字作为前缀(第一个参数类型也称为扩展类型,即指方法对这个类型进行扩展);
(4)第一个参数不能使用任何其他的修饰符(如不能使用ref、out等修饰符);
(5)第一个参数的类型不能是指针类型。
这些规则都是硬性规定,无论方法违反了哪一条,编译器都可能会报错,或认为它不是一个扩展方法。
5.对象的复制和序列化
对象的深复制:生成两个独立的对象,而非索引,修改其中一个的内容并不影响另一个。
序列化:把程序中对象的相关数据保存到文件中去。
反序列化:在需要的时候根据保存在文件中的数据得到原来对象的精确副本。
序列化的目的:一是以某种存储形式使自定义对象持久化;二是将对象从一个地方传递到另一个地方。
序列化和反序列化的前提:要将对象的类声明为可序列化。
.NET支持序列化的三种形式:二进制序列化、XML序列化、*SOAP序列化
注意:采用XML序列化的方式只能保存public的数据成员和可读写的属性,对于private等类型的数据成员不能进行序列化。
static: 静态,可以修饰类、字段、属性、方法
sealed: 密封,由它修饰的类或方法将不能被继承或是重写。
internal:内部,限定的是只有在同一程序集中才可访问,可以跨类
abstract:抽象,可修饰类、方法、属性。详细看https://blog.csdn.net/yiyelanxin/article/details/73870651
virtual:虚拟,默认情况下类中的成员都是非虚拟的,通常将类中的成员定义成虚拟的,表示这些成员将会在继承后重写其中的内容。virtual 关键字能修饰方法、属性、索引器以及事件等,用到父类的成员中。需要注意的是,virtual 关键字不能修饰使用 static 修饰的成员。
override:重写,是在子类中重写父类中的方法,用于扩展或修改继承的方法、属性、索引器或事件的抽象或虚拟实现。提供从基类继承的成员的新实现,而通过override声明重写的方法称为基方法。
深拷贝与浅拷贝的区别:
深拷贝完全将对象中的所有字段都复制到副本对象中,不管拷贝的对象是引用类型字段还是值类型字段,都会被重新创建并复制,副本对象内的值并不会因为源对象数据的值的修改而改变;
浅拷贝和深拷贝的不同之处就在于,同样都是完全将对象的所有字段都复制到副本对象中,值类型被复制之后,在源数据内修改,副本的值不发生改变,但是复制的为引用类型的值的时候,由于浅拷贝只复制引用类型值的引用,所以当源数据中引用类型的值发生改变时,副本中的数据也会发生改变。
另外,由于String类型理论上是引用类型,但是由于该引用类型的特殊性,Object.MemberwiseClone方法仍旧为他创建了副本,也就是说,在浅拷贝过程中,我们应该将字符串看成值类型。
抽象方法和虚方法的区别 :
~抽象方法和虚方法的区别在于:虚拟方法有一个实现部分,并为派生类提供了覆盖该方法的选项,相反,抽象方法没有提供实现部分,强制派生类覆盖方法(否则 派生类不能成为具体类);抽象方法不能实例化,要子类必须强制性的覆盖它的方法。而虚方法则是提供了选择,可以覆盖可以不覆盖,
继承基类中的虚方法。
~abstract方法只能在抽象类中声明,虚方法则不是;
~abstract方法必须在派生类中重写,而virtual则不必;
~abstract方法不能声明方法实体,虚方法则可以。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?