c#基础提纲
2024-03-28 11:26:09 0 举报
AI智能生成
c#基础内容笔记
作者其他创作
大纲/内容
数据类型
变量是存放数据的地方,简称数据;方法(旧称为函数)是处理数据的逻辑,又称算法。
值类型的实际数据直接存储在栈上,引用类型在栈中存储一个引用地址,地址指向托管堆中实际存储的数据
引用类型变量的赋值只复制对对象的引用,不复制对象本身。值类型变量赋给另一个值类型变量时,将复制包含的值
值类型
结构体(数值类型如int,long,bool等、用户定义的结构体),枚举,可空类型
引用类型
数组,类,接口,委托,object,字符串
内存泄漏指的是堆里面的数据没有及时清理(C#有自动清理机制,一般不需要特别关注)
栈溢出(Stack Overflow)指的是由于程序不合理,将栈的内存占用完了。
装箱与拆箱
object
C#中值类型和引用类型的最终基类都是Object类型,导致值类型也可以当做引用类型来处理(子转父)
装箱
将值类型转换为引用类型 int val =10;object obj = val;(子转父自动)
拆箱
引用类型转换为值类型 int num = (int) obj;(父转子强转)
装过箱的对象才能被拆箱
变量variable
7种变量
静态变量
类定义中使用static关键字声明,作用域是整个程序,又称为静态字段或类变量
静态变量的作用域是整个程序,它们在程序的生命周期内一直存在,直到应用程序域被卸载。
实例变量
字段或者属性,又称为成员变量
局部变量
方法、构造函数或访问器内定义的变量,只在定义它们的区域内可见,stack上分配内存
数组
存储一系列相同类型的数据
引用类型
形参、ref引用参数、out输出参数
值类型变量
值类型变量的地址(stack)里面储存的就是数据
引用变量
引用类型变量的地址(stack)存储的是数据(heap)的地址
全局变量
在类和结构体之外定义的变量默认就是全局的,存储在静态存储区,而不是在堆或栈上
全局变量可以在任何类或方法之外定义,但通常建议将它们放在静态类中,以避免全局命名空间的污染。
常量const
常量默认为static可以通过类名直接访问,属于类而不是对象实例即没有实例常量;常量名通常使用大写字母,
readonly
运行时常量,程序运行时赋值且之后无法更改,又称只读变量
只能在类中声明,可以在声明的同时初始化或者在静态构造函数中进行初始化
const
编译时常量,程序编译时将对常量值进行解析,并将所有常量引用替换为相应值
Const常量既可以声明在类中也可以在函数体内,必须在声明的同时完成初始化
默认为静态类型,不能手动再为Const增加一个Static修饰符
枚举enum
枚举的本质是限制了取值范围的整数。枚举符号不能相同,但是符号对应的值可以相同
在类中定义或者在命名空间下定义,而不能将其定义到方法中
枚举本身仅能用Public(默认)和internal修饰;枚举的成员始终是公开的,不能有访问修饰符
枚举中值的数据类型只能是整数类型,包括 byte、short、int、long;
每个没有指定值的枚举值,它的初始值都是上一个枚举类型的值加 1。
结构体struct
可实现接口,但是不能继承类/结构体;不能有显式无参构造器,有参的构造函数可以
结构体的定义位置与枚举类型一样,都是在类中定义或者在命名空间下定义,而不能将其定义到方法中
结构体的成员可以是基础数据成员,也可以是结构体类型成员(不能是本结构体)
静态static
静态成员
静态成员是属于类的,通过类名直接访问;静态成员只创建一次,在类第一次被访问的时候创建
静态方法中不能直接访问实例成员,因为当静态方法执行的时候,实例成员完全有可能还没有存在
实例方法中可以直接访问静态成员,因为当实例方法存在的时候,静态成员一定存在了
静态成员既不在栈也不在堆中,创建在静态存储区,知道程序结束才会被销毁。
实例成员
类的实例叫做对象
没有被static修饰的成员叫做实例成员;实例成员是属于对象的,通过对象名去访问实例成员。
创建对象的时候,实例成员跟随着对象一起创建在堆中;有多少个对象,实例成员就有多少份。
对象被回收,这个对象中的实例成员就跟随着一起回收
静态类
类如果被static修饰,那么这个类就叫做静态类;静态类中的所有成员要求都是静态成员static
静态类不能创建对象。因为没有实例成员,就算可以创建对象,也没有任何的实际意义
静态类不能有构造函数
方法function
函数作为类(或结构体)的成员时才被称作方法即成员函数。
方法是面向对象范畴的概念,在非面向对象语言中仍然称为函数,前身是C/C++语言的函数
方法表示类(或结构体)能做什么事情,可以隐藏复杂的逻辑,达到复用的目的
匿名方法Anonymous
匿名方法是通过使用delegate关键字创建委托实例来声明的。delegate (){}
是否指定返回类型都行,可以从方法主体内的return语句推断返回值。
匿名方法经常用来传递代码块作为委托参数,实例化定义好的委托
扩展方法
扩展方法是静态类的静态方法public static,其中this修饰符应用于第一个参数
扩展方法既可以当成扩展类型的实例方法调用,也可以当成静态类的静态方法调用
参数传递
值参数
调用方法时为每个值参数创建一个新的存储位置,实际参数的值会复制给形参
形参的值发生改变时,不会影响实参的值,使用的是两个不同内存中的值
引用参数ref
引用类型中形参和实参指向是同一个地址,所以会改变函数体外声明的实际参数。
如果在函数体内部对引用参数再次new实例化,实参也会指向新的地址新的数据
ref是有进有出,out是只出不进;由于out、ref编译时的处理方式相同,方法重载时无法区分
输出参数out
out形参必须在其方法内进行初始化,
实参无论在外部是否初始化传入方法内部都会被清空
数组类型参数
必须是形参列表中的最后一个而且只能有一个
数组类型的形参使用params修饰后,实参无需再次声明,可以直接在方法中传入数组中的元素
具名参数
调用方法时候,传入的参数前面可以带着名字
具名调用可以增加方法的可读性,而且参数不再受前后位置约束了。
可选参数
参数因为设置了默认值而可选;可选参数不可用ref,out等修饰符
当参数为部分可选时,可选参数 的声明必须定义在不可选参数的后面
可选参数的初始值必须是一个在编译期可确定的常量,如果是除String之外的引用类型默认值只能是null
运算符
运算符的本质是函数(即算法)的“简记法”运算符不能脱离与它关联的数据类型
创建运算符
int? 表示可空的整型相当于Nullable<int> x = null;整型原本是不能赋于null的,引用类型默认值就是null
?:是if{}else{} 的简单形式,x?y:z 表示 x 为 true返回 y,x 为 false返回 z;string s = (x>60)?"cf":"ada"
a??b 当 a 为 null 时则返回 b,a 不为 null 时则返回 a 本身;空合并为右结合运算符
??= 仅当左操作数的计算结果为 null 时,才会将其右操作数的值赋值给其左操作数numbers ??= new List<int>()
?. 如果对象为 NULL,则不进行后面的获取成员的运算,直接返回 NULL; int? firstX = points?.FirstOrDefault()?.X;
语句
语句:是高级语言的语法-汇编语言和机器语言只有指令;高级语言中的表达式对应低级语言中的指令
循环语句
do语句至少执行一次,while语句要先判断条件
for语句适合执行次数固定的循环。判断条件是否符合→环体执行→循环变量增减→判断条件
for循环的三个条件都可以不写,但是;是必须的。
foreach语句用来遍历集合(实现了IEnumerable接口),本质就是迭代器的简写
可以直接使用迭代器对集合进行遍历。数组T[],泛型列表List<T>都实现了IEnumerable接口
选择语句
虽然if语句可以嵌套,但是那样写代码不够清晰,通常使用 if(){} else if(){}语句
swith后面不能是浮点类型;case后面必须跟一个常量表达式;需要主动break;可以配合goto语句转到对应的case
跳转语句
continue跳出本次循环,break跳出整个循环语句(注意嵌套循环只跳出当前)
goto语句将控制转到由标签标记的语句
throw语句把捕捉到的异常抛给程序调用的上一级,交给上一级调用者cath并处理异常
try后面catch可以是多个语句,捕捉多个异常但是同时只能执行一个;也可以写一个通用catch捕捉任何异常
可以有零个或者一个finally语法;一般会在finally语句写log日志或者释放资源dispose,其永远都会执行
字段与属性
属性是字段的包装器,通过get和set方法来操作对应的字段
属性能够限制只能给字段赋于某个范围的值;要求字段只能读或只能写;在改变字段时能改变对象的其他一些状态
当字段需要为外部提供数据的时候,将字段封装为属性,而不是使用公有字段(public)
如果只定义get方法,那么这个相应变量就是只读的;如果只定义set方法,那么相应变量就是只写的
set和get块中属性必需有一个,因为即不能读又不能写的属性是没有意义的
字段可以在声明的时候初始化,跟在构造函数里面初始化没有区别;无显式初始化时,字段获得其类型的默认值
箭头函数简化属性
指定属性初始值列表的方式来初始化对象
委托delegate
定义
委托是函数指针的升级版,一种特殊的类,也是引用类型;委托是对方法的抽象和封装,间接的调用方法
委托使得一个方法可以作为另一个方法的参数进行传递
变量(数据)是以某个地址为起点的一段内存中所存储的值
方法(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
实例化委托时传入的方法必须与委托定义时的返回值类型和参数个数、类型和顺序相同
使用委托可以将同类型(参数,返回值)的方法绑定到同一个变量上,当调用此变量时就可以调用绑定的方法
接调用是通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行然后返回
间接调用是通过函数指针调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行然后返回
命名方法委托
命名方法委托的一个实例只注册了一个方法(用=赋值)
多播委托
使用某个方法实例化委托后,就可以使用+或者-来实现添加或撤销其他返回值和参数列表相同的方法
多播委托调用时,注册的所有方法都将被执行。
未调用委托时也可以撤销已经注册的方法。orderDelegate -= Order.BuyFlower;
多播委托还有一个缺陷,一旦一个委托发生异常,其他委托都会停止
匿名委托
匿名委托的本质是在实例化委托时候用的是匿名方法即代码块
匿名方法可以提通过使用 delegate 关键字声明,不过现在一般都是用箭头函数代替了
匿名方法中不需要指定返回类型,它是从方法主体内的 return 语句推断的
同步异步调用
每一个运行的程序是一个进程(process),每个进程可以有一个或者多个线程(thread)
同步调用都是在同一线程内,异步调用的底层原理是多线程
直接同步调用:使用方法名调用委托
间接同步调用:使用单播/多播委托的Invoke方法
隐式异步调用:使用委托的BeginInvoke
显式异步调用:使用Thread或者Task
避免委托滥用
应当适时使用接口取代委托。Java完全使用接口取代了委托的功能。
事件event
定义
事件使对象或者类具备通知能力;对象拥有一个事件,当事件发生的时候,对象能够通知其他对象
事件的本质是委托字段的一个包装器,对委托字段的访问起限制作用
事件对外界隐藏了委托实例的大部分功能,仅暴露订阅/移除事件处理器的功能
事件是委托字段的包装器(add/remove语法糖),用来保护字段不被滥用,包装器永远都不可能是被包装的东西
事件一种引用类型,实际上是一种特殊的委托。 访问修饰符 event 委托名 事件名
事件模型
五个部分组成
事件流程
1.我有一个事件(声明事件)
2.一个人或者一群人关心我都这个事件(订阅事件)
3.我的这个事件发生了(触发事件)
4.关心这个事件的人会被依次通知到(消息通知)
5.被通知到的人根据拿到的事件信息(事件参数)对事件进行响应(事件处理)
使用事项
挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名
一个事件可以注册多个事件处理器;一个事件处理器也可以被多个事件挂接
事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检测
事件处理器是成员方法;事件可以同步调用也可以异步调用
不仅要定义事件,还要去触发,触发事件一定是事件拥有者的内部逻辑
事件声明
委托是一种特殊的引用类型,声明要放在类的外面,然而事件作为委托的包装器,其声明是要放在类里面
.net已经为我们准备好了事件所使用的委托,public delegate void EventHandler(object sender,EventArgs e)
用于声明Foo事件的委托,一般命名为FooEventHandler
可以把委托的参数列表看成是事件发生后,发送给事件响应者的“事件消息”
FooEventHandler委托单参数:
第一个是object类型,名字为sender,实际上就是事件的拥有者source
第二个是EventArgs类的派生类,类名一般为FooEventArgs,参数名为e。
严格来说事件只能出现在 +=/-=操作付的左边,不能Invoke直接调用(只能委托),但有时候会与语法糖冲突不得已
1.事件的拥有者
2.事件处理者
3.注册事件并触发
变量作用域
c#没有全局变量的概念,我们常常使用静态变量代替全局
同名的局部变量不能在同一个作用域内声明两次
只要类在某个作用域内, 其字段也在该作用域内
局部变量存在于声明该变量的块语句或方法结束的右花括号之前的作用域内
在 for while 或类似语句中声明的局部变量存在于该循环体内
访问级别
五种修饰符
public可以被任意存取
protected只可以被本类和其继承子类存取;
internal只可以被本组合体/本项目(Assembly/dll/exe)内所有的类存取
protected internal 唯一的一种组合限制修饰符,可以被本组合体内所有的类和这些类的继承子类所存取。
private只可以被本类所存取
默认访问权限
类、接口默认是internal类型的,也可以显示的定义为public类型,不允许是其他访问类型
类里面,属性和方法默认是private的;我们一般把属性定义为public,字段定义为private
类默认是internal类型的;类中所有的成员,默认均为private;嵌套类的默认访问权限是private
其他修饰符
new修饰符只能用于嵌套的类,表示对继承父类同名类型的隐藏
override 只能用于嵌套的类,表示对继承父类同名类型的覆盖
abstract用来修饰抽象类,表示该类只能作为父类被用于继承,而不能进行对象实例化
abstract不能和new同时用
ealed用来修饰类为密封类,阻止该类被继承。同时对一个类作abstract和sealed的修饰是没有意义的
嵌套类
将类定义在另一个类的内部,被包含的类称作嵌套类,而包含嵌套类的类就被称为外部类
嵌套类中只能访问外部类中的静态成员,不能直接访问外部类的非静态成员
外部类只能通过内部类的实例来访问内部类的public成员,不能访问protected,private
在外部类与嵌套类的非静态成员可以重名,因为他们不是同一个类,各自属于各自的
嵌套类的默认访问权限是private
想要在作用域范围之外引用嵌套类,需要使用类似Outer.Nesting的完整限定名方式
匿名类
无需显式定义一个类,就可以将一组只读属性封装到单个对象中;
如果某个类的实例只会用到一次,就可以使用匿名类的方式创建实例
由于匿名类没有类名,可以声明为var类型,编译器会根据匿名类中属性的值来确定属性的类型并生成一个类
同其他类一样,所有的匿名类均继承自System.Object类
匿名类型包含一个或多个公共只读属性
静态类
定义
静态类仅包含静态成员,无法实例化即不能定义构造函数。
普通类中可以有静态方法,但是静态类中的方法、变量都必须是静态的
静态类的本质,是一个抽象的密封类,所以不能被继承,也不能被实例化
如果一个类下面的所有成员,都需要被共享,那么可以把这个类定义为静态类
静态方法
使用static修改的方法就是静态方法,否则就是非静态方法
非静态方法可以访问类中的任何成员,静态方法只能访问类中的静态成员
静态成员属于类所有,非静态成员属于类的实例所有。
类的静态成员在内存中只占同一块区域。
静态方法效率上要比实例化高,静态方法的缺点是不自动进行销毁
静态变量可以先声明再赋值
重载overload
同一个作用域内(比如一个类里面),定义一系列同名方法,但是方法的参数列表不同。
通过传递不同的参数来决定到底调用哪一个
方法重载只有通过参数不同来判断调用哪个方法,而不能通过返回值
型出现后,相同参数,相同返回值的方法也能构成重载
类与继承
定义
类是一种数据类型,是一种抽象数据结构,代表了现实世界某个东西的种类
数据成员:常量、字段和属性
函数成员:方法、属性、事件、索引器、运算符、实例构造函数、静态构造函数和析构函数
类支持继承,使得派生类可以对基类进行扩展和专用化。
声明
类一般声明在命名空间内;类也可以声明在另一类中(嵌套类/成员类)
如果没定义任何构造函数,编译器会自动生成一个无参构造函数把属性设置为默认值(整型0,引用null)
一旦定义了构造函数,编译器不再自动生成,如果定义了有参构造函数,实例化对象时仍想使用无参,需要显示定义无参构造
继承
继承就是子类在完整接收父类前提下,对父类进行纵向扩展(类成员的重写)和横向扩展(类成员个数的扩充)
只能继承一个基类,但可以实现多个其接口Interface;
密封类sealed类不能被继承;构造器的不可继承性;子类的访问权限不能超过父类;
派生类的实例也是基类的实例,因此可以用父类类型的变量引用子类类型的实例
构造函数的调用
父类的构造器不会被子类继承
派生类实例化时默认先隐式调用基类的无参构造函数再调用自己的构造函数。
可以base关键字显式调用基类的某个无参或者有参构造函数
base关键字访问基类对象,但是只能向上访问一层
父类
子类
重写与多态
重写override
继承时发生,在子类中重新定义父类中的方法。
父类方法用virtual标记为虚方法,子类方法override标记为重写
只有为虚方法时可以进行重写,不能对非虚方法进行重写
子类方法必须和父类被重写的方法名字、参数、返回值完全匹配
被overide修饰的方法依然可以被子类再次重写;除了方法,属性也可以被重写
当用virtual修饰后,不允许再有 static、abstract 或者 override 修饰符;
虚方法一般在基类定义,在派生类中重写
虚方法必须有方法体,不能像抽象方法那样简写
public abstract void Color();public virtual void Color(){ }
多态
基于重写机制(virtual→override),函数成员的具体行为由实例化对象决定
当我们用父类变量引用子类实例,调用被重写的成员时,总是调用子类的版本
方法重载时父类的虚方法有机会被调用(base),如果不打算调用可以定义为抽象方法abstract
如果派生类重写了父类方法,父类变量引用派生类实例时,将会调用派生类中的重写后的方法,这就是多态
父类
子类
多态
隐藏
子类中使用new关键字对父类的同名成员的隐藏
子类方法必须和父类的方法名字、参数、返回值完全匹配
不需要virtual/override关键字
接口和抽象类
区别
接口是完全未实现逻辑的”类“,抽象类是一个未完全实现逻辑的类
接口定义抽象规则,只包含方法、属性、索引器、事件的签名;抽象类不仅定义规则,还能提供已实现的成员
如果一个抽象类里面只有抽象方法,那么换成接口就行了;属性也可以抽象,不过我们一般只抽象方法
接口为解耦而生:高内聚,低耦合,方便单元测试;抽象类为复用而生:专门作为基类来使用,也具有解耦功能
接口和抽象类都不能实例化,只能用来声明变量
接口interface
接口中只能包含方法、属性、事件和索引的组合,不能定义字段和包含实现方法
接口一旦被实现,实现类必须实现接口中的所有成员方法,除非实现类本身是抽象类。
C# 是单继承,接口是解决类可以同时继承多个基类的问题
接口成员都是public abstract且不能显式声明
子类实现接口中的方法就相当于是重写override,那么子类实例赋予父接口的时候具有的是子类实现后的方法
接口中只包含成员的签名,接口没有构造函数,不能直接使用 new 对接口进行实例化
抽象类abstract
有抽象属性或抽象方法的类一定是抽象类,抽象类中的属性或方法不一定都是抽象的。
抽象类可以继承抽象类,子类如果不是抽象类,则必须重写(override)抽象基类中的所有抽象属性和抽象方法
如果一个抽象类里面只有抽象方法,那么换成接口就行了
抽象方法只有方法定义没有方法体{};抽象类不能实例化
属性也可以抽象,不过我们一般只抽象方法
重载虚函数也可以达到多态的目的,区别在于父类的虚函数仍然可以被调用(base)
基类与派生类转换
基类对象可以指向任意一个派生类对象,派生类可以强制转换为基类。
派生类对象转换为基类对象向上转换,一般是隐式转换基类对象 = 派生类对象
基类对象转换为派生类对象向下转换,需要配合异常处理进行强制转换派生类对象 = (派生类名)基类对象
先使用is和as再进行强制转换,能够更加安全地进行转换,转换错误后程序能继续运行下去,不用抛出异常
如果派生类重写了父类方法,父类变量引用派生类实例时,将会调用派生类中的重写后的方法,这就是多态
泛型Generic
定义
泛型是一种创建可重用组件的技术,它允许在类、方法、接口和委托中定义类型参数
定义的时候没有指定具体的参数类型,把参数类型的声明推迟到了调用的时候才指定参数类型
使用泛型主要是为了避免成员膨胀或者类型膨胀
泛型类
泛型类或方法使用类型参数(通常用大写字母开头,如 T, U, V 等),在实例化或调用时可以指定具体的类型。
泛型类或方法就可以操作任何类型的对象,而不需要进行类型转换或使用 object 类型。
泛型方法
泛型方法允许在方法中定义类型参数,而不是在类中。
这使得方法可以操作任何类型的对象,而不需要在每次调用时指定类型参数。
事实上调用的时候方法后面的<T>也是可以省略的,由编译器进行推断
泛型约束
使用where关键字,加上约束的条件,使类型T必须遵循一定的规则
箭头函数
一个Inline匿名函数,用它可以高效简化代码,常用作委托,回调
如果编译器能够根据上下文推断出输入参数的类型,这时可以省略参数类型的声明
函数体只有一个语句时不需要{ };不需要Return,由编译器自己推断
Lambda 表达式是可以访问到外部变量的,可以将此表达式分配给委托类型
如果需要指定输入参数的类型,则必须对每个参数执行类型声明,不能只指定其中的一个。
传入参数只有一个时,可省略很多,不需要括号,不需要return关键字,编译器会自动完成添加
如果多个参数或者没有参数,则必须加括号
属性赋值简写;方法体简写
Lamda表达式实例化委托
作为Linq语句中的运算符
常见
构造函数
实例构造函数
构造函数没有返回值(void也不需要);命名必须和类名相同;通常使用构造函数来进行类中字段的赋初值
构造函数重载:根据参数个数、参数顺序、参数类型不同、参数种类不同(值,ref,out)、泛型参数数量不同来区分(返回值不行)
类(非静态)未定义任何构造函数时,编译器自动生成默认的隐式无参构造函数,初始化字段的值(整型0引用Null)
类定义了有参构造函数,实例化时需要调用无参构造函数,必须再定义一个显式无参数构造函数(编译器不在自动生成了)
构造函数默认是private即私有构造函数;如果希望能够从类的外部创建类的实例,需要将构造函数声明为public;
调用派生类的构造函数时会先调用基类的无参构造函数,如果基类没有提供无参数构造函数,派生类必须使用 base 显式调用基类构造函数
私有构造函数
静态构造函数
部分类partial
特性Attribute
分支主题
分支主题
分支主题
分支主题
0 条评论
下一页