C#(Csharp)学习这一篇文章就够了!!

avatar
作者
猴君
阅读量:1

在学唐老狮的课程 顺便ab自己记录一下

C#是面向对象语言,也具备三大特征,ab会从这里开始。

C#——封装:

1.类和对象:

类的声明类对象 声明是两个概念。

类的声明: 相当于自定义一个变量类型。

类对象:是从类里创建出来的,而创建对象的过程称之为实例化对象。

2.成员变量和访问修饰符:

成员变量:要声明在类中,用来描述对象的特征,可以是任意变量类型(可以在一个类中声明别的类),同时成员变量的数量不做限制,而是否要赋值根据需求决定。

成员变量的默认值: 值类型 数字类型都是0,bool类型为false,引用类型为null。

注意: 

如果要在类中声明一个和自己相同类型的成员变量时,不能对它进行实例化。

访问修饰符:

public —公共的:自己(内部)和别人(外部)都能访问和使用。‘

private —私有的: 自己(内部)才能访问和使用;不写的时候默认为private。

protected —保护的: 自己(内部)和子类才能访问和使用。

3.成员方法:

成员方法(函数)用来表现对象行为,并且受到访问修饰符的影响,返回值参数不做限定,成员方法的数量也不做限制。

注意:

成员方法不能加Static关键字,使用时必须实例化出对象,再通过对象使用(可以想成某个对象执行了某个行为)。

练习:

定义一个老师类和学生类,再定义一个实物类,有名字,并让他们联系起来

如:老师吃了什么什么,学生吃了什么什么。

4.构造函数:

在实例化对象(new对象)时,会调用用于初始化的函数,就叫做构造函数。在不写构造函数的时候,会有一个默认的无参构造函数。

构造函数的写法:没有返回值,函数名必须和类名相同,没有特殊需求时都是public,并且构造函数可以被重载。

注意:

如果自己没有写无参构造函数,却写了有参构造函数,默认的无参构造函数会被顶掉

练习:

5.成员属性:

是用于保护成员变量的,为成员变量的获取和赋值添加逻辑处理,也可以解决public,private,protected的局限性。属性可以让成员变量在外部(可读不可写),(可读只有本类可写)等等.

上面就是一个属性,相当于把小写的name包裹了一层。

get和set可以只写一个

注意:

1. 当get set前面什么都不写时,会使用声明时的访问权限。

2.加的访问修饰符要低于属性的访问权限。(get和set的访问权限不能比声明变量时的大)

 

3.不能让get和set的权限都低于属性的权限。(两个都写就让声明的访问修饰符没用了) 

6.静态成员:

用Static修饰的成员变量,方法,属性等,都叫做静态成员。

静态成员可以直接通过类名点出来使用

静态成员属于这个类!并不属于这个类的实例对象。

注意:

静态成员在程序运行后就会存在。静态函数中不能使用非静态成员(因为成员变量要将对象实例化出来后,才能点出来使用,不能无中生有),想要使用可以在静态函数中实例化一个。

报错

没报错

说的更细一点就是 静态成员方法在程序一运行就有了,但是你还没有声明别的变量,所以此时不能使用非静态变量(无中生有了就是), 但是如果你在静态成员方法中实例化了过了,那就能用。

常量和静态变量的区别:

相同点:他们都可以通过类名点出来使用。

不同点:1.const必须初始化 不能修改,而static没有这个限制。

                2.const只能修饰变量,static可以修饰很多。

                3.const一定是卸载访问修饰符后的,static没有这个要求。

7.静态类和静态构造函数:

静态类:

就是class前面加了static,静态类只能包含静态成员,不能被实例化。

作用:工具类,拓展方法用。

静态构造函数:

就是在构造函数上加static。可以在静态构造函数中初始化静态变量。

注意:

1.静态类和非静态类都可以有。

2.不能使用访问修饰符。

3.不能有参数

4.只会自动调用一次。(静态构造函数只会在第一次使用这个类时调用一次)

作用:初始化静态成员

8.拓展方法:

为现有 非静态 变量类型 添加新方法。

为非静态变量类型 添加方法,也就是说要从实例点出来用。

注意:

1.一定是写在静态类中。

2.一定是个静态函数

3.第一个参数为拓展目标(类)

4.第一个参数用this修饰

练习:

为整形拓展一个求平方的方法:


9.内部类和分布类(partial):

内部类:

在类的内部声明的类,使用时要用包裹的类点出自己,访问修饰符的作用很大。

如:

分布类(partial):

把一个类分成几部分声明,增加拓展性。

注意:

分布类可以写在多个脚本中,并且他们的访问修饰符要一致,最后不能有重复的成员。

C#——继承:

1.继承的基本原则:

一个类A继承一个类B,类A将会继承类B的所有成员,A类将拥有B类的所有特征行为。

被继承的类:叫做父类基类

继承的类:叫做子类派生类

子类拥有父类所有特征和行为,并且子类可以有自己的特征和行为,也受到访问修饰符的影响。

public—公共,内外部访问。

private—私有,内部访问。

protected—保护,内部和子类访问。

internal—内部,只有在同一个程序集的文件中,内部类型或者是成员才能访问。

特点:

1.单根性:子类只能由一个父类。

2.传递性:子类可以间接继承父类的父类。

2.里氏替换原则:

里氏替换原则是面向对象七大原则中最重要的原则。

即:任何父类出现的地方,子类都可以代替(因为子类对象包含了父类所有内容),父类容器装子类对象

这里用父类装载了子类(Player)那他可以用Player类的成员方法吗?

显然是不行的,方法是在子类里的,而你声明的是父类对象。

父类 对象名 = new 子类();

        这时就要用到 ↓ 下面的知识点 ↓

is和as:

is:用于判断,判断一个对象是否是指定类对象。        返回值:bool,是为true,不是false。

as:用于转换,将一个对象转换为指定类对象。           返回值:指定类型对象(转换成功时),失败则返回null。

练习:

写一个Monster类,派生出Boss和Goblin两个类,Boss有技能;小怪有攻击;随机生成十个怪,装载到数组中,最后遍历这个数组调用他们的攻击方法。

3.继承中的构造函数:

当声明一个子类对象时,会先执行父类的构造函数,再执行子类的构造函数。

父类的父类的构造函数—→ 父类构造函数—→子类构造函数。(执行顺序) 

如图:

这个类还是Grandpa但是 new的是son所以会调用上面的构造函数。

通过base可以调用父类的构造函数(类型要一样吗,如下图)

输出结果会打印出ab

注意:

1.父类的无参构造函数很重要(被顶掉的话)

2.子类可以通过base代表父类 调用父类构造函数。

练习:

有一个打工人基类,有工种、工作内容两个特征,一个工作方法,

程序员、策划、美术分别继承打工人,用继承中的构造函数知识点

实例化三个对象,分别是程序员、策划、美术。

可以想象成 子类的无参构造函数 调用了父类的有参构造函数 而传递的参数就是自己后面定的。

4.万物之父object和装箱拆箱:

万物之父object:

关键字是object,它是所有类型的基类,是引用类型。

可以利用里氏替换原则,用object容器装所有对象。也可以用来表示不确定类型,作为函数参数类型。

当用object储存值类型时:(用强转)

当用object储存引用类型时:(用is和as来判断和转换)

装箱和拆箱:

发生条件:

装箱:用object存值类型

值类型引用类型存储,栈内存会迁移到堆内存中。

拆箱:再把object转为值类型

把引用类型存储的值取出来,堆内存会迁移到栈内存中。

好处:不确定类型时可以方便参数的存储和传递。

坏处:存在内存迁移,层架性能消耗。

5.密封类sealed:

密封类:使用关键字sealed修饰的,会让该类无法再被继承。(可以理解为让类 结扎)

在面向对象程序设计中,密封类主要作用就是不允许最底层子类被继承,可以保证程序的规范性,安全性。

C#——多态:

1.多态概念:

字面意思就是“多种状态”。

继承同一个父类的子类们,在执行相同方法的时有不同的表现。

(同一父类的对象          执行相同行为(方法)      有不同的表现

让同一个对象有唯一行为的特征。

多态的实现:

运行时多态(vob,抽象函数,接口)。

vob中:

v:virtual(虚函数):用来给子类重写的。

o:override(重写):跟virtual配套出现的,用来重写方法。

b:base(父类):代表父类,通过base来保留父类行为。

练习:

创建一个图形类,有求周长和求面积两个方法。

创建矩形类和圆形类继承图形类。

实例化矩形,圆形对象求面积周长。

2.抽象类和抽象方法(abstract):

抽象类:

被关键字abstract修饰的类,就叫抽象类。

不能被实例化、可以包含抽象方法、继承抽象必须重写其抽象方法。

抽象类中,封装所有的知识都可以写在其中(包括里氏替换原则)!!

抽象函数:

抽象方法又叫纯虚方法,即用abstract关键字 修饰的方法。

只能在抽象类中声明、没有方法体、不能是私有的、继承后必须用override重写。

抽象方法(abstract)和虚方法(virtual)的区别:

 

抽象方法(abstra):

1.不能有方法体,而且必须被子类重写。

2.只能声明在抽象类中。

虚方法(virtual):

1.虚方法是有方法体的,也可以调用,可以被子类选择性重写。

2.虚方法可以在任何非密封类中声明。

共同点:

1.都可以无限被子类重写。

2.都可以base重用。

注意:

如何选择使用普通类还是抽象类呢?

不希望被实例化的对象,相对于比较抽象的类可以用抽象类。

父类中的行为不太需要被实现的,只希望子类定义具体规则的 可以选择使用抽象类。

(用于整体框架设计 会使用)

3.接口(interface):

接口是行为的抽象规范!!!!

是一种自定义类型,关键字为:interface。

接口声明规范:

1.不包含成员变量。

2.只包含方法、属性、索引器、事件。

3.成员不能被实现。(不能写方法体)

4.成员可以不用写访问修饰符,不能是私有的。(此处不写默认是public)

5.接口不能继承类,但是可以继承另一个接口。

接口使用规范:

1.类可以继承多个接口。(类只能继承一个类)

2.类继承接口后,必须实现接口中所有成员。

特点:

1.和类的声明类似。

2.接口是用来继承的。

3.接口不能被实例化,但是可以作为容器存储对象。

通过接口 飞机 跟鸟类建立起了联系(让不同种类的子类有联系),就可以用里氏转换原则,用一个接口父类,存储不同类型的对象。

IFly f =  new (飞机);     or   IFly f =  new (鸟);   

里氏转换原则 用IFly来存储不同种类 又有相同行为的对象。

当接口继承接口时:

接口继承接口时 不需要实现。

等待 类继承接口后  类自己去实现所有内容。

接口总结:

4.密封方法:

用密封关键字sealed修饰的重写函数,让虚方法(virtual)或者抽象方法(abstract)之后不能再被重写。(会和override一起出现)

C#——面向对象关联的一些知识点:

1.命名空间:

命名空间是用来组织和重用代码的,用来包裹类的。就像是一个工具包,类就像是一件一件的工具,都是声明在命名空间中的。

语法:

namespace  命名空间名

{

        class 类名

        class 类名

}

注意:

1.命名空间是可以分开声明的(和分布类一样),但是里面的类名不能有重复的。

2.不同命名空间中相互使用 需要引用命名空间或指明出处(即使是写在同一个脚本中)

3.不同命名空间中 允许有同名类。

4.命名空间可以包裹命名空间。(using 包裹的命名空间名.被包裹的)

2.万物之父(objcet)中的方法:

万物之父 object。是所有类型的基类(引用类型)

可以利用里氏替换原则装载一切对象。存在装箱拆箱。

1.object中的静态方法:

Equals:判断两个对象是否相等。 返回bool

最终判断权交给左侧对象的Equals方法,不管值类型or引用类型都会按照左侧对象的Equals方法的规则来进行比较。(引用类型判断相等,是看有没有指向同一个内存地址,并不是字面上的同一个类)。

ReferenceEquals:比较两个对象是否是相同的引用。

主要用来比较引用类型的对象。值类型对象的返回值始终是false。

Equals是比值类型的,ReferenceEquals是比引用类型的。

2.objcet中的成员方法:

GetType:用于获取对象运行时的类型Type,通过Type结合反射相关知识可以做很多关于对象的操作。

MemberwiseClone:用于获取对象的浅拷贝对象。(理解为克隆的不干净,克隆了一个引用类型对象和老的一样,值类型是可以改的)

说白了就是会返回一个新的对象,但是新的对象中的引用类型会和老对象中一致(也就是指向同一个内存地址)。

        不能通过 对象.出来用 是因为他是保护(propted)类型。

3.object中的虚方法:

虚方法Equals:

我们可以重写该方法,定义自己比较相等的规则。

虚方法GetHashCode:

该方法是获取对象的哈希码。通过重写该函数来自己定义对象的哈希码算法。

虚方法ToString:

用于返回当前对象代表的字符串。通过重写该函数来自己定义对象转字符的规则。

在打印方法时,默认使用的就是对象的Tostring方法打印出来的内容。

练习:

一个Monster的类的引用对象a,Monster类有攻击力,防御力,技能名,血量等属性。

我想复制一个和a对象一模一样的b对象,并且改变b的属性a不会受到影响怎么办?

3.结构体和类的区别:

结构体和类的最大区别是在存储空间上的,因为结构体是值类型,类是引用类型。因此他们的存储位置一个是在栈上,一个是在堆上。

两者在使用上很相似,结构体甚至可以用面向对象的思想来形容一类对象。

但是 结构体只具备封装的特性,并不具备继承和多态的特性,因此大大减少了它的使用频率。

由于结构体不具备继承和多态,所以他不能够使用protected访问修饰符。

细节区分:

1.结构体是值类型所以在栈中。类是引用类型所以在堆中。

2.结构体不具备继承和多态的特性,不能用protected访问修饰符。类则全具备三大特征。

3.结构体成员变量声明不能指定初始值。类可以。

4.结构体不能声明无参的构造函数。类可以。

5.结构体声明有参构造函数后,无参构造函数不会被顶掉。而类的会被顶掉。

6.结构体需要在构造函数中初始化所有变量成员,而类随意。

7.结构体不能被继承。类可以。

8.结构体不能声明析构函数。类可以。

9.结构体不能被Static修饰。类可以。

10.结构体不能在自己内部声明和自己一样的结构体变量。类可以。

注意:结构体可以继承接口。

在什么时候使用结构体和类呢?

1.想要用继承和多态时,直接淘汰结构体 用类,比如玩家,怪物等等。

2.对象是数据集合时 优先考虑结构体,比如位置、坐标等等。

3.从值类型和引用类型赋值时的区别上去考虑,比如经常被赋值传递的对象,并且改变赋值对象,原对象不想跟着变化时,就用结构体,比如坐标、向量、旋转等等。



4.抽象类和接口的区别和相同点:

抽象类(abstract):

abstract修饰的类和方法。

抽象类不能被实例化

抽象方法只能在抽象类中声明 是个纯虚方法,必须在子类中实现。

接口(interface):

interface 自定义类型。

是行为的抽象。

不包含成员变量,仅包含方法,属性,索引器,事件。成员都不能实现,不写访问修饰符时默认为public。

抽象类和接口的相同点:

1.都可以被继承。

2.都不能直接实例化。

3.都可以包含方法声明。

4.子类必须实现未实现的方法。

5.都遵循里氏替换原则。

抽象类和接口的不同点:

1.抽象类中可以有构造函数;接口则不能。

2.抽象类只能被单一继承,接口则可以被继承多个。

3.抽象类中可以有成员变量;接口则不能。

4.抽象类方法可以使用访问修饰符;接口则建议不写 ,因为默认public。

5.抽象类中可以声明成员方法、虚方法、抽象方法 静态方法;接口则只能声明没有实现的抽象方法。

什么时候用抽象类:表示对象的用抽象类。

什么时候用接口:表示行为拓展的用接口。不同对象拥有的共同行为,可以使用接口来实现。

比如:动物是一类对象,可以选择抽象类;而飞翔是一个行为,并不是所有动物都会飞翔,就可以用接口。

5.UML类图:

使用一些UML可视化软件,不用写代码,通过一些图标相关内容就可以直接生成代码,在其基础上进行开发。(最终目的就是能通过图形就把业务逻辑就完成了)

但是本文中的UML类图是uml其中很小的一部分,学习它的目的是为了帮助我们在进行面向对象程序开发时,更好的理清对象关系,养成面向对象编程的习惯。

就用VISIO。

(设置方法)

如:

6.面向对象七大原则:

一:单一职责原则(SRP) Single Responsibility Principle

类被修改的几率很大,因此应该专注于单一功能。如果把多个功能放在同一个类里,功能之间就形成了关联,改变其中一个功能时有可能会终止另一个功能。

如:策划、程序、美术是三个类,他们应该各司其职,在程序语言的世界中只做自己应该做的。

二:开闭原则(OOP)Open Closed Principle

对拓展开发,对修改关闭。

拓展开发:模块的行为可以被拓展从而满足新的需求。

修改关闭:不允许修改模块的源代码(或尽量使修改最小化)。

如:继承就是最经典的开闭原则的体现,可以通过添加新的子类和重写父类的方法来实现。

三:里氏替换原则(LSP)Liskov Substitution Principle

任何父类出现的地方,子类都可以代替(用父类容器装载子类对象)

四:依赖倒转原则(DIP)Dependence Inversion Principle

要依赖于抽象,不要依赖于具体的实现。

人要开枪,是依赖于枪械,但是我并没有直接去依赖这些枪械,利用里氏替换原则,倒转去依赖了这个开枪接口。

五:迪米特原则(LoP)Law of Demeter

又称最少知识原则,一个对象应该对其他对象尽可能的少了解。

:一个对象中的成员,要尽可能少的直接和其它类建立关系,目的是降低耦合性。

六:接口分离原则(LSP) Interface Segregation Principle

不应该强迫别人依赖他们不需要使用的方法。

一个接口不需要提供太多的行为,应该尽量只提供一个对外的功能,让别人去选择需要实现什么样的行为,而不是把所有的行为都封装到一个接口中。

:飞机接口、走路接口、跑步接口等等虽然他们都是移动的行为,但是我们应该把他们分为一个一个单独的接口,让别人去选择使用。

七:合成复用原则(CRP)Composite Reuse Principle

尽量使用对象组合,而不是继承来达到复用的目的,继承关系是强耦合,组合关系是低耦合。

:脸应该是嘴巴、鼻子、眼睛、耳朵的组合,而不是依次继承。角色和装备也应该是组合,而不是继承。

注意:不能盲目使用合成复用原则,要在遵循迪米特原则的前提下。

okk本文到这里可能(我也不确定?)就结束了,后面还会有C#进阶的课程我再接着做笔记。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!