《C#程序设计教程 -李春保》阅读笔记
( 需注意程度:红>粗体>下划线,蓝色:我的疑问 )
- 老师的引言
- [师]对待一种新语言的关注点
- 数据类型定义(python不用定义)
- 语法
- JAVA两大优势
- 面相对象:比C++简单
- 跨平台
- 开源:造就了很多免费的东西
- 每个button其实就是类的实例
- 找工作一定要有一技之长
- 面试内容:
- 大公司:基础:C语言、数据结构
- 小公司:技术:C#、Java
- 数据传递(不确定正误):
- 多数据传递:静态类
- 单数句传递:构造函数
- 看懂书了再写代码
- [师]对待一种新语言的关注点
- 1 C#语言概述
- C#:
- 完全面向对象,设计一个程序就是设计一个或多个类
- 最长编写的3种应用类型
- 控制台程序
- 窗体程序:窗体为.cs文件
- web应用程序
- 代码:
- 托管代码:
- 在CLR控制下运行
- 不能写内存
- 是安全的
- 非托管代码:
- 由操作系统直接运行
- 可以使用指针操作内存(所以指针是在操作内存)
- 不安全的
- 托管代码:
- .NET Framework
- FCL(Framework Class Library):全面的类库,包含大量处理常见的低级编程操作;由命名空间组成见p4-5
- CLR(公共语言运行库):操作系统顶层,管理程序的执行,总的来说,包括:无用存储单元的回收,代码验证和类型安全,代码访问安全
- CLS(Common Language Specification公共语言规范):定义了所有编程语言必须遵守的共同标准,语法
- CTS(Common Type System通用类型系统):定义了一套预定义数据类型。如:VB中的integer和C#中的int,在编译后都映射为System.Int32,实现了不同语言数据类型的统一,从而实现跨语言功能
- 运行应用程序代码的过程
- C#与. NET Framework的关系
- Visual C#集成开发环境
- 菜单
- 工具栏:菜单中常用的按钮放到这里了,可在“视图|工具栏”自定(我也要养成自定义工具栏的思维)
- 解决方案管理器:
- 解决方案名称。解决方案包含项目。.slh可以打开整个项目
- 项目:包含于解决方案
- 引用:库函数,指出程序引用的命名空间
- program.cs是代码文件。所有C#代码的源文件都是.cs(即C Sharp)为拓展名
- 编辑器设置:在 工具|选项(虽然是在菜单的某一选项,却包含了整个软件的设置)
- 项目的构成(目录在 资源管理器 显示)
- properties:
- 引用:指出程序引用的命名空间
- Program.cs
- namespace关键字的作用(using某些方面类似于c的#include):
- 组织代码
- 分类管理
- 包含多个类
- namespace关键字的作用(using某些方面类似于c的#include):
- 其他
- Main()为程序入口
- 数据类型
- 值类型:直接存储所包含的值(结构体是值类型),在栈空间分配
- 引用类型:存的是指针,指向它所要存储的值(字符串是引用类型),其对象在堆空间分配
- C#:
- 2 C#程序设计基础
- 变量的四个理解层面
- 变量的名称:和该内存空间绑定在一起,对变量名称的操作就是对内存空间的操作,也就是说程序员通过变量来使用计算机的内存空间。从系统的角度看,变量就是程序中的存储单元,它既表示这块内存空间的地址,也表示这块内存空间中存储的数据。
- 变量的值
- 变量的数据类型:决定了可容纳什么样的值,取值范围,什么样的操作可执行
- 变量的作用域和生命周期
- 数据类型:1值类型变量的内存开销小、访问速度快; 2引用类型变量内存开销大、访问速度稍慢; 3无论值类型变量还是引用类型变量,都是在栈空间中分配对应的存储空间,所不同的是,引用类型变量所指的对象是在托管堆上分配内存空间
- 值类型
- 简单类型:[师]类型转换是一定要考虑溢出
- 结构类型:注意结构体是值类型
- 枚举类型
- 引用类型
- 类
- string类:@ 字符串 为严格字符串,其中无转义字符
- 接口
- 数组
- 委托
- 类
- 值类型
- 堆与栈:
- 栈:系统管理所有的栈空间
- 堆:程序可在堆中保存数据,但在c#中不能显式地删除他们,因为我们没有指针,我们只管创建类的实例用就好了,CLR帮我们处理无用的对象
- 其他
- 拆箱和装箱:即值类型和引用类型的转换
- 结构类型:实际问题中,一组数据往往具有不同的数据类型,结构类型可以存储多个不同类型的数据(我:分类是人类认识世界的方式,所以这个也是为了归类,为了理清人类的思绪?),结构类型常规使用就是赋值和访问
- 枚举类型:用符号代替数据,提高程序的可读性。如用X.red表示红色。枚举类型常规使用就是赋值和访问
- 运算符:
- new:创建对象(我:这也当作运算符看?那什么不是运算符呢?)
- checked:启用溢出检查
- 常用类:1 C#是一种纯面向对象语言,使用类和结构体来实现数据类型,而对象是给定数据类型的实例;2 C#中一切都是对象,例如,int数据类型就是一个类,它提供了相应的属性和方法,int x=3这条代码,int为类,x为对象。
- string类:
- 格式化字符串:Format()。有的函数库中就有了,不要再造*了
- Math类:要写计算器,根本不用自己实现那些复杂算法,直接用这些函数就可以了,一定要有这种思想,不要重复造*
- Convert类:值类型(?仅值类型吗?书写错了?string也可以的啊)间的转换
- string类:
- 问题:类语言和非类语言编译器的实现有什么区别?
- 问题:静态方法和动态方法有什么区别,为什么要做这种区分?
- 变量的四个理解层面
- 3 C#控制语句
- [师] if{ if..... }else{ if..... }如果有很多判断,写在黑体的if不易读,写在划线的if更易读
- 循环控制语句:for通常用于已知循环次数(for只能用于已知循环次数,这句话是错的)
- [师] 我(俞定国老师)当年是纸上写一遍,脑子验证,然后敲在电脑
- break,continue用于跳出循环(故写在if中,跳出的是if外的循环)
- 阅读代码的方法:阅读代码只看关键的,细节都忽略
- while后的表达式为逻辑表达式或关系表达式
- 4 数组和集合
- [师] c的强势在于
- 指针
- 位运算:直接到低层内存的运算
- [师]1先写各种代码,然后向上向下拓展。2代码要分块,也就是把步骤和功能分割开
- 数组
- 数组是引用类型,通过一个名称来存取,所有值放在内存的一段连续空间,为了区分不同的值,添加了一些索引或下标。
- 集合和数组不同的地方在于:可动态增减
- 通常把二维数组叫做矩阵
- 数组的动态初始化(new)和静态初始化,只不过是编译时间不同,结果是一样的
- foreach(var 迭代变量 in 数组或集合)
- 有些情况为了简单,数组下标中的0总是不用
- Array类:所有数组的抽象基类。在C#中,数组实际上是对象,而不像C/C++中那样的可寻址连续内存区域
- (我:对象不是 可寻址连续内存区域 ?)(我:什么是对象,什么是可寻址连续内存区域)。
- 静态方法+动态方法
- ArrayList类 [师]:重点掌握,不像C需要自己控制
- Array类的优化版本,容量或元素个数不像Array是固定的
- 可存放多重数据类型,其用的数据类型是Object
- Array是多维的,ArrayList是一维的
- 均非静态方法
- List<T>类:是ArrayList类的泛型等效类 [师]:重点掌握,不像C需要自己控制
- 数据类型是指定的
- 均为非静态方法
- 这个有一个方法叫ForEach可以对每个元素执行指定操作
- 数组、List和ArrayList的区别
- 数组在内存中是连续存储的,所以它的索引速度是非常的快,而且赋值与修改元素也很简单,但是数组也存在一些不足的地方。比如在数组的两个数据间插入数据也是很麻烦的,还有我们在声明数组的时候,必须同时指明数组的长度,数组的长度过长,会造成内存浪费,数组和长度过短,会造成数据溢出的错误。这样如果在声明数组时我们并不清楚数组的长度,就变的很麻烦了。C#中最先提供了ArrayList对象来克服这些缺点
- ArrayList是数据存储和检索的专用类,大小是按照其中存储的数据来动态扩充与收缩的。因为ArrayList会把所有插入其中的数据都当作为object类型来处理。这样,在我们使用ArrayList中的数据来处理问题的时候,很可能会报类型不匹配的错误,也就是说ArrayList不是类型安全的。既使我们保证在插入数据的时候都很小心,都有插入了同一类型的数据,但在使用的时候,我们也需要将它们转化为对应的原类型来处理。这就需要装箱与拆箱的操作,会带来很大的性能损耗。(该类的方法索引均从0开始)
- 所以在C#2.0后出现了泛型的概念。List<T>类 在声明List集合时,我们同时需要为其声明List集合内数据的对象类型。通过允许指定泛型类或方法操作的特定类型,泛型功能将类型安全的任务从您转移给了编译器。不需要编写代码来检测数据类型是否正确,因为会在编译时强制使用正确的数据类型。减少了类型强制转换的需要和运行时错误的可能性。泛型提供了类型安全但没有增加多个实现的开销。(List list = new ArrayList();这句创建了一个ArrayList的对象后把上溯到了List。此时它是一个List对象了,有些ArrayList有但是List没有的属性和方法,它就不能再用了。 而ArrayList list=new ArrayList();创建一对象则保留了ArrayList的所有属性。 )
- 考试前要看一遍正确的定义数组的几种方式,否则会混淆!考后这句话就删除掉吧
- [师] c的强势在于
- 5 面向对象程序设计
- [师]:面向对象中,有20%的知识常用,就像搭积木,你只需要搞定用的东西
- 对象之间通过一定的“相互操作”传递信息([师金哲凡]:传递消息是一种设计概念,落实到语言上就是函数调用),在消息的作用下完成特定功能。
- 封装:尚未理解
- C#实现运行时多态性的基础是动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制。(不能完全理解后面这句话的意义)
- 从计算机语言角度来说,类是一种数据类型,而对象是具有这种类型的变量
- 通常用数据成员模拟现实世界事物的特性,通常用函数成员模拟现实世界的功能和操作
- 无修饰符时,默认为私有的
- 类和结构类型很相似,但也有不同,主要差异如下
- 对象浏览器和类图方便程序员进行面向对象的程序设计
- 对象引用变量在栈空间分配,实例在堆空间分配
- 没有引用变量指向的堆空间会被CLR收回
- using 命名空间,注意using不是加的类,而是命名空间
- 当定义类对象时,构造函数会被自动执行,当类对象销毁时,析构函数会自动执行
- 自己写了含参数构造函数,记得自己写一个默认函数
- 静态成员属于类所有,非静态成员属于类的对象所有。提出静态成员概念的目的是为了解决数据共享的问题
- 静态和非静态的存储方式
- 静态构造函数的特点:不会继承,在所有的构造函数中最先被执行
- 问题:属性是强化的字段,弱化的方法,right?
- Main()是每个C#应用程序入口,在启动时由公共语言运行库调用
- 方法中的参数是保证不同方法间互动的重要“桥梁”,方便用户对数据进行操作
- 方法的参数类型(注意参数是值还是引用就容易多了)
- 值类型
- 在栈中为形式参数分配空间
- 计算实参的值,并把该值复制给形式参数
- 注意体会这张图和代码
- 引用参数
- 不会在栈上分配空间
- 形参将作为实参的别名
- 注意体会这张图和代码
- 输出参数
- 也不开辟新的内存区域
- 参数数组
- 可选参数
- 值类型
- this关键字:对当前实例的引用
- 对象的复制:非静态方法Object.MemberwiseClone()来实现
- 浅复制:共享除String的所有引用成员实例
- 深复制:需自己创建新的引用类型,C#并没有嵌入在语言中
- 嵌套类:未细看,用时看p119-p121
- 索引器:类似于属性,不同在于访问器采用参数。索引类型并不限制为整数。它的引入为了使程序更加直观、易于理解。索引器的声明、使用如下:
- 委托:即函数指针,不同于c/c++,可以与多个方法关联在一起,可以把委托对象看成一系列方法,使用. +=委托对象 .增加方法,委托封装的多个方法都会被一次性一起调用。声明与使用:
- 事件:委托(函数指针)是事件的基础。在事件通信中,事件源类不知道哪个对象或方法将接受到(处理)它引发的事件,所需要的是在源和接收方之间存在一个媒介(或类似指针的机制)。使用与分析见下图:
- 犯错截图:
- 6 继承和接口设计(习题尚未做)
- 继承
- 为了对现实世界中的层次结构进行模型化,面向对象的程序设计技术引入了继承的概念
- 派生类对象也是基类的对象,所以基类的引用变量可以引用基派生类对象(注意勿反,这两句话句话和一般的世界规律有点不同)
- 除了构造函数和析构函数,派生类隐式地继承了基类所有成员
- 基类的private不能被继承(待查询)
- (问题:那上一句话是不是说错了?“除了构造函数和析构函数,派生类隐式地继承了基类所有成员”)
- 构造函数:从最远开始调用
- 析构函数:从最近开始调用
- (问题:一个类只能有1个析构函数,为什么?不是可以两个吗?自己的一个,CLR默认的都有一个?)
- 继承使得程序允许创建分等级阶层的类
- 【师】:默认构造方法建议都写上,不要忽略
- 多态性
- 多态性是指发出同样的消息(如方法调用)被不同类型的对象接收时可能导致不同行为
- 隐藏基类方法:用new关键字(注意:并非直接重写,有意隐藏要用new)
- 重写基类方法:适用情景:同一签名的方法实现相同的目标,但在不同的地方有不同的实现细节,此时就需要用虚方法实现该方法的不同细节。 调用虚方法时,首先调用派生类中的该重写成员,如果没有派生类重写该成员,则它可能是原始成员
- 在基类使用virtual关键字(不能与static、abstract、override一起用)
- 在子类使用override关键字
- 一个基类对象引用变量可以指向其子类的对象(记忆方法:父亲可以指着儿子(骂))
- is运算符:检查是否为该类型或可以转换为制定类型
- as运算符:在兼容的引用类型之间执行转换
- 易混的重写和重载对比
- 重载:同一个类,同一个方法的多种形式,签名不同
- 重写:父子类, 同一个方法重写, 签名相同
- 抽象类
- 一个类不与具体的事物相联系,而是表达一种抽象的概念,仅仅作为其派生类的一个基类
- 派生抽象类的非抽象子类必须实现所有抽象方法和抽象访问器
- 尚未理解内容(下方):
- 其他注意事项:
- 接口
- 接口是类之间交互内容的一个抽象,把类之间需要交互的内容抽象出来定义成接口,可以更好地控制类之间的逻辑交互。接口最适合为不相关的类提供通用的功能。一个接口可以被多个类继承,在这些类中实现该接口的成员,这样接口就起到了提供统一界面的作用。
- 接口不包含方法的实现
- 接口不能定义字段、构造函数、常量和委托。
- 所有接口成员默认是公有的,在接口中声明任何修饰符都是错误的。
- 接口和抽象类的异同
- 接口的实现:
- 隐式实现(即实现不包含接口完整名称):接口成员必须是公共的、非静态的
- 显式实现(即实现包含接口完整名称):不能使用任何修饰符。(如果没有充分理由,应避免显式定义接口成员)
- 接口映射:接口通过类实现,对于接口声明中的每一个成员都应该对应类的一个成员,这种对应关系由接口映射来实现。
- 一个接口成员确定哪一个类的成员呢?假设类C实现了接口Ia的一个接口fun,此时fun的映射过程如下:
- ①如果类C中存在一个显式成员实现,它与Ia的接口成员的fun相应,则由它来实现fun成员。
- ②如果在类C中找不到匹配的显式接口成员实现,则看类C中是否存在一个与fun相匹配的非静态的公有成员,若有,则认为是Ia的接口成员的fun的实现。
- ③如果以上都不满足,则在类C的基类(父类)中寻找一个基类D,用D来代替C进行重复寻找,直到找到一个满足条件的类成员实现。如果都没找到,则报告一个错误。
- 一个接口成员确定哪一个类的成员呢?假设类C实现了接口Ia的一个接口fun,此时fun的映射过程如下:
- 看书p162-p164三个例子,接口实现的继承很容易混。
- 上图162页第一个例子仍未搞懂
- 接口在集合排序中的应用
- Array.Sort():使用每个元素的ICompareable接口实现对整个ArrayList中元素的排序
- Array.Sort(IComparer):使用指定的比较器。(把接口对象作为参数传递给Array.Sort())
- 继承
- 7
- 8
- 9 Windows应用程序设计
- 熟练掌握各个控件的属性设置和方法调用是采用C#语言设计界面友好的应用程序的基础(黑体中的两点即为主要学习内容)
- 窗体是一个容器
- 通过设置控件的属性可以改变其外观和标题信息(属性和外观是一体两面的东西)
- 每个窗体对应3个文件,即两个。cs文件和一个。resx文件。前者为C#文件,后者为资源文件。
- 窗体各事件的引发顺序
- 焦点与Tab键次序:视图 | Tab键顺序
- 常用的控件设计
- 所有控件基本属性
- Name属性:通过此名称来引用这个控件
- Text属性:获取用户输入或显示文本
- Size、Location
- Font
- BackColor、ForeColor
- Cursor:获取或设置 鼠标的光标
- Visible、Enabled:Enabled决定了该控件是否被使用
- 控件的外观和行为(如控件的大小、颜色、位置以及控件的使用方式等)是由它的属性决定,不同的控件拥有不同的属性,并且系统为它们提供的默认值不同。
- 控件RichTextBox
- 控件GroupBox分组框:用于分组(视觉上:归类;功能上:单选)
- 控件CheckBox复选框:
- 属性Checked:获取或设置一个布尔值
- MessageBox(并不是该控件,不过是书中在此介绍):其返回值DialogResult,是通过事件来返回的。
- 控件RadioButton单选按钮:搭配GroupBox一起使用
- 控件PictureBox
- 作用
- 做容器
- 放图片
- 属性Image:Image.FromFile函数加载图像(路径用绝对路径不好)
- (问题1:如果书中没有出现这段话,怎么知道有这个方法呢?!)
- (问题2:属性.方法 属性和方法什么关系啊?!)
- 作用
- 控件ComboBox组合框
- Items的属性和方法
- 控件ListBox列表框
- 属性SelectionMode:值MultiExtended,为允许搭配shift或ctrl键的功能
- 事件KeyPress:在控件有焦点的情况下按下任何按键时发生
- 例子:
- 控件CheckListBox:继承自ListBox,增添了复选框功能
- 控件Timer定时器(学会用库很重要!我原来不知道竟然想自己写一个定时器):
- 属性Interval:两个定时器事件之间的时间间隔
- 其他备注:
- 所有控件基本属性
- 多个窗体之间的数据传递
- 通过静态字段传递数据
- 通过构造函数传递数据(把数据放在参数中)
- 创建事件过程(多个事件可以连接到单个事件过程)
- 在Windows窗体创建
- 在执行时为Windows窗体创建
- 10 用户界面设计
- 菜单:
- 分类
- 下拉式菜单:MenuStrip控件
- 弹出式菜单(右键弹出的菜单):需要将Form的ContextMenuStip属性设置好,才能弹出
- 每个菜单项都是一个ToolStripMenuItem对象,可以把菜单它理解为容器。菜单项唯一的事件就是Click事件
- 热键设置:菜单的文字,&后加的字母 将作为该菜单项的热键
- 分类
- 通用对话框用:用ShowDialog方法显示
- OpenFileDialog:打开文件:返回值为DialogResult
- SaveFileDialog:保存文件
- ColorDialog:选择颜色
- FontDialog:选择字符
- PrintDialog:打印文件
- PrintViewDialog:打印预览
- 图像列表框控件:ImageList
- 专门负责管理图片
- 有几套需要管理的,就创造几个对象来管理
- 非可视化控件
- 树形视图控件:TreeView
- (问题:P271,方法和事件到底有什么区别,为什么分开说)
- TreeView中的结点的组织关系是父结点 管理 子结点(管理即:添加、删除)
- Nodes是TreeView控件的一个属性,它也是一个结点集合,每个结点就是一个Tree Node对象
- TreeNode对象的属性和方法:P272
- 列表视图控件:ListView
- 可包含多项,由Items集合表示,每一项就是一个ListViewItem对象,而每一个ListViewItem由其SubItems集合表示
- ListView的Columns集合属性,由ColumnHeader对象组成
- 工具栏控件(本质:图片按钮):ToolStip控件
- 状态栏控件(和Label差不多):StatusStip
- 菜单:
- 11
- 12 文件操作
- 输入:文件中的数据读取到内存中
- System.IO模型提供了一个面向对象的方法来访问文件系统。以流(Stream)的方式对各种数据进行访问,这种方式不仅灵活,而且可以保证编程接口的统一。
- (问题:体现在哪?以前没有流操作是怎样的?)
- 文件操作的基本方式:
- 用File类打开文件
- 建立对应的文件流,即FileStream对象
- 用StreamReader/StreamWriter类对文件(文本文件)流读/写 or 用BinaryReader/BinaryWriter类对文件(二进制文件)流读/写
- 文件夹和文件的操作
- 文件夹的操作:
- Directory类:均为静态方法
- DirectoryInfo类:均为非静态方法
- 文件的操作:
- File类:均为静态方法
- FileInfo类:均为非静态方法
- 文件夹的操作:
- 文件流的产生-FileStream类
- 文本文件的操作-StreamReader/StreamWriter类
- 二进制文件的操作-BinaryReader/BinaryWriter类
- 序列化和反序列化:将对象的公共字段、私有字段、类的名称(没有方法、事件)转换成字节流,然后写入数据流,反序列话反之。使用序列化可以将随想从一个应用程序传递给另一个应用程序。实现方法如下: