前言

Winform 虽然看起来过时了,事实上也确实过时了,但是就目前为止,凭借着性能不错,简单易用高效的特点在工控行业仍然有一定的应用,所以说如果准备搞工控上位机开发,仍然有一定的学习价值。8月份花了一个多月的时间完成了C#语法特性的学习和理解(C#图解教程【经典】),本月计划以项目为驱动完成C# WinForm的学习,主要参考资料为 WinForm程序设计与实践(廉龙颖)+C# WinForm图形界面GUI编程(B站)

学习资料
学习规划
  • 第一天
    • 第一章 .NET简介及其开发环境(快速浏览略去)
    • 第二章 C#语言基础(快速浏览略去)
    • 第三章 面向对象程序设计(快速浏览略去)
  • 第二天
    • 第四章 WinForm基础
  • 第三天
    • 第五章 输入与输出
  • 第四天
    • 第六章 数据访问技术
  • 第五天
    • 第七章 进程与线程
  • 第六天
    • 第八章 加密与解密
  • 第七天
    • 第九章 GDI+
  • 第八天
    • 第十章 Windows应用程序打包
  • 第九天
    • 第十一章 实践项目-酒店管理系统
学习成果检验

个人设计:密码管理器
主要功能及特性:
1.保存我们各大网站的账号密码信息
2.自动生成高可靠性高强度的密码
3.密码信息本地混淆加密保存
4.密码信息支持通过WebDev坚果云备份
5.密码管理器软件支持锁定,防止密码被旁人看到

工业级项目理解:高速摄像焊接视觉采集系统

第一章 .NET简介及其开发环境

  • .NET、.NET Framework、Visual Studio和C#之间的关系
    答:.NET平台是应用程序开发平台,用来构建和运行Microsoft Windows和Web程序;.NET平台的核心是.NET Framwork,它为.NET平台下应用程序提供基本框架(主要是公共语言运行库和基础类库);Visual Studio是目前最流行的.NET应用程序集成开发环境;C#是专为.NET设计的面向Internet和企业级应用的编程语言。

  • Visual Studio的一些实用使用技巧
    答:1.使用Ctrl+K组合键和Ctrl+D组合键,自动调整代码缩进,可以提高代码的规范性和可读性;2.选中需要查询帮助的C#关键词按F1可以获取微软在线帮助程序;3.#region 说明文字和#endregion组合使用可以进行代码折叠,方便快速定位和后期维护。

第二章 C#语言基础

  • 定义标识符时建议遵守的规范
    答:1.使用Pascal命名法定义类名、方法名和属性名,即每个单词的首字母要大写,如Age;2.使用Camel命名法定义字段名和变量名,即首字母小写,之后每个单词的首字母均为大写,字段名前加下划线"_",如变量arrayList、字段_userName;3.常量名的所有字母都大写,且单词之间使用下划线连接,如USER_AGE;4.在定义标识符时,推荐使用代表某种意义的英文单词,增加可读性。5.程序中的标识符不能以关键词命名,除非加上@前缀

  • 数据类型中的值类型和引用类型的区别
    答:值类型直接包含数据;而引用类型的变量存储对数据的引用,后者称为对象。对于值类型,每个变量都有自己的数据副本,各变量之间的操作互不影响;对于引用类型,同一个对象可能被多个变量引用,因此一个变量的操作可能影响另一个变量对该对象的引用。在定义一个值类型变量后,将直接为该类型分配空间,可以直接赋值和使用;而引用类型在定义时并不会分配空间,只有在对其实例化时,才真正分配存储空间。

  • C#中字段和局部变量的概念
    答:字段在在类或结构中“直接声明”的任意类型的变量,字段是通过“访问修饰符 变量类型 标识符”来声明的,同一个类中不能有两个标识符相同的字段;局部变量是在方法“主体“中声明的变量,局部变量是通过”变量类型 标识符“来声明的,在同一个类中可以有很多标识符相同的局部变量。

  • 用float型定义变量:float x= 3.14是否正确?
    答:不正确,赋值运算符(=)左右两边的精度类型不匹配。在默认情况下,包括小数点的实数,如本题中的3.14,被存储为double类型(即双精度),而float类型定义的变量,如本题中的x,即是单精度的。如果想让上面的语句编译正确,应该对赋值运算符(=)右边的值做强制类型转换,即把常量3.14强制转换为单精度(即float类型)如float x = (float)3.14; 或者,一开始就把3.14存储为单精度类型,在3.14后面加小写字母"f"或者大写字母"F",如float x = 3.14F。

  • 显式转换和隐式转换
    答:第一点:隐式转换是安全的类型转换(将赋值运算符右边长度更小的数据类型转换为左边长度更长的数据类型),由系统自动进行,通常不会丢失任何信息或者丢失准确率,但不会丢失一个数量级;显式转换(将赋值运算符右边长度更长的数据类型转换为左边长度更小的数据类型)由用户使用强制转换运算符来实现,可能会导致数据丢失或异常。第二点:在默认情况下,包含小数点的实数,被系统存储为double类型。所以1.float x = 3.1+9错误;2.float x = 3.1f + 9正确;3.float x = (float)(3.1 + 9)正确;4.double x = 3.1 + 9正确。

  • 逻辑运算符(&&,||)和(&,|)的区别
    答:(&&,||)这两个逻辑运算符,一旦左边表达式的计算结果已经可以决定整个表达式的布尔值,就不会再计算右边的表达式,而直接返回布尔结果;而(&,|)这两个运算符(用作布尔与,布尔或的情况下),会先计算左边表达式,然后计算右边表达式。最后通过两个表达式之间的布尔逻辑运算符对两个表达式计算,返回布尔结果。补充:其中(&,|)还有按位与、按位或的功能。

  • switch条件语句需要注意的地方
    答:1.对于多分支结构,建议多使用switch语句,使用if...else语句嵌套编写的程序不仅可读性不高且执行效率偏低。2.在C#中,当语句块不为空时,C#要求每个标签项后必须使用break语句或者goto语句跳出整个switch语句,而不允许从一个case自动遍历到其它case。(这一点同C和C++语言有显著不同)

  • foreach条件语句需要注意的地方
    答:1.foreach语句总是遍历整个集合。如果只需要遍历集合的特定部分或者需要绕过特定元素,又或者需要反向遍历,最好使用for语句。2.如果循环体需要知道元素索引,而不仅是元素值,则必须使用for语句。3.foreach语句读出的元素变量是一个只读变量,不能进行修改,如果需要修改元素,必须使用for语句。

  • continue语句和break语句的区别
    答:break语句可直接跳出当前的整个循环(使得程序的执行流程从循环内转到循环外),可用在循环和switch语句中;continue语句不是终止并跳出当前循环,而是终止执行continue语句后面的语句,直接回到循环的起始处,开始下一次循环(将程序的执行流程提前跳转到下一次循环,执行流程仍然在循环内),只能用在循环中。

  • try语句需要注意的地方
    答:1.try和catch后的一对{}是必要的,即使代码块只有一条语句。2.可以有一个或多个catch块(但是只能有一个通用catch语句(就是catch后面不带任何内容,既没有指定异常类型,也没有指定异常对象名这样的语句),且这条通用catch语句只能放在最后面),但由于catch语句是依照它们出现的次序进行检查,进而确定哪一个catch处理异常,所以,必须首先将最特定的异常类型放在最前面,然后逐步到比较通用的异常。按照最如果某一catch语句指定的类型同它之前的catch语句指定的类型一致且由此类型派生而来,那么运行时就会发生错误。

第三章 面向对象程序设计

  • public int BufferSize { get; set; }与public int BufferSize;有什么区别?
    答:就使用上并没有不同,但是如果不加的话它是个成员变量(字段),如果加了之后就是属性(访问器),编译器会为你生成两个对应的函数。且根据Framework Design Guideline,public field必须是readonly的,定义为属性可以遵循这一准则;遵循设计准则也有助于其他开发者快速理解你写的代码。另外,在涉及wpf属性绑定,在自动属性是可以在set和get块前面放一个访问修饰符的。详见: https://www.zhihu.com/question/410257728/answer/1375205727

  • 方法调用的三种方式
    答:1.在同一个类中,方法可以直接调用。2.在其它类中的方法,需要通过类的实例进行调用。3.静态方法需要通过类名进行调用。

  • 方法的高级参数需要注意的地方

    • params参数:用于实现方法接收任意个数的参数
      答:1.一个方法中最多只能出现一个params。2.params关键词只能放在所以参数的最后面。3.当参数为params修饰时,要防止外界传入非法参数,如null。
    • ref参数:用于传递参数的引用,而不是参数的值
    • out参数:用于将值从方法体内传到方法外
      答:1.由于ref和out同属按引用传递,因此不能通过ref和out的不同实现重载,即不能定义两个完全一样的方法,仅有参数ref和out不同。2.不使用ref或out修饰的参数,不一定就是按值传递的。如数组本身就是引用类型,故不必使用ref修饰也是按引用传递的。
  • 静态成员和非静态成员的区别(以及何时应该设置为静态成员)
    答:静态成员属于类,而不属于类的实例,因此,不要通过类而不通过类的实例来访问;而非静态成员总是与特定的实例(对象)相联系。在实际应用中,当类的成员引用或操作的信息属于类而不属于类的实例时,就应该设置为静态成员。

  • 抽象类和类的区别与联系
    答:抽象类是类,可以包含具体的功能实现代码(与接口不一样,接口绝对不能包含实现代码)。抽象类又与类不同。首先,它以abstract修饰;其次,抽象类不能被实例化,只能供其它类继承;再次,含有抽象方法的类一定要声明为抽象类,但反过来不成立,即抽象类可以不包含抽象方法。

  • 虚方法和抽象方法的区别
    答:1.虚方法必须有实现部分,抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化。2.抽象方法只能在抽象类中声明,虚方法不是。如果类包含抽象方法,那么该类也是抽象的,也必须声明类是抽象的。3.抽象方法必须在派生类中重写,这一点和接口类似,虚方法不需要再派生类中重写。简单说,抽象方法是需要子类去实现的。虚方法是已经实现了的,可以被子类覆盖,也可以不覆盖,取决于需求。抽象方法和虚方法都可以供派生类重写。

  • 关于委托需要知道的东西
    答:委托是一种动态调用方法的类型,委托对象本质上代表着方法的引用。具有以下特定:1.委托不同于C++的函数指针,委托是面向对象的,是安全的数据类型。2.委托允许将方法作为参数进行传递。3.委托可用于定义回调方法。4.委托可以把多个方法链接在一起,这样,在事件出发时,可同时启动多个事件处理程序。

  • 关于Lambda表达式需要知道的东西
    答:Lambda表达式本质上就是匿名方法(提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法。在匿名方法中您不需要指定返回类型,它是从方法主体内的 return语句推断的。),只是将匿名方法的书写方式进一步简化。由于方法需要依附于委托,故Lambda表达式的书写也要遵从委托的限制。

第四章 WinForm基础

  • 事件处理程序通常包含哪几个部分
    答:事件处理程序通常有两个参数:第一个参数引用触发事件的控件;第二个参数封装了事件的一些状态信息。不同事件的处理过程,其第二个参数的类型页不同,相应的内部封装的状态信息也不同。

  • MenuStrip控件的小贴士
    答:在输入标题内容时,可以在标题内容的某个字母前加“&”。例如,“窗体(&F)”命令将具有一个快捷键Alt+F,程序运行时,快捷键Alt+F同样可以执行此菜单命令。

相关说明:由于本章为基础内容,且很多控件或者组件直接通过可视化的设计界面很容易进行配置和了解,故不做过多的摘记了,还是要多用才知道。可以参见b站的视频。

第五章 输入与输出

  • 文件与流的区别和联系
    答:从严格意义上讲,文件指的是放在磁盘上的静态信息,这种信息不是连续的,是随机的;流是字节序列的抽象概念,流提供一种向后续存储器写入字节或从后续存储器读取字节的方法。流一般指的是连续的字节信息。总结:文件是存储在存储介质上的数据集,是静态的,具有名称和相应的路径。当打开一个文件并对其进行读写时,该文件就成为流。流和文件指的是都是一件事物,但是状态不一样。

  • System.IO命名空间的类
    答:1.Directory,DirectoryInfo:创建、删除并移动目录,通过属性获取特定目录的相关信息。2.File,FileInfo:创建、删除并移动,通过属性获取特定文件的相关信息。3.StreamReader、StreamWriter:读写文本数据信息。4.BinaryReader、BinaryWriter:读写二进制数据。

  • Directory和DirectoryInfo类的区别
    答:两者功能相似,不同的是DirectoryInfo类是一个实例类,所有方法都是实例方法。也就是说,要想使用DirectoryInfo类提供的方法必须实例化一个属于DirectoryInfo类的对象。因此,如果需要对同一目录进行多次重复操作时,应该考虑使用DirectoryInfo类的实例方法。且DirectoryInfo还具有一些特殊的属性。

第六章 数据访问技术

第六章到第十一章的学习全部在我的作品密码管理器中体现

第七章 进程与线程

第六章到第十一章的学习全部在我的作品密码管理器中体现

第八章 加密与解密

第六章到第十一章的学习全部在我的作品密码管理器中体现

第九章 GDI+

第六章到第十一章的学习全部在我的作品密码管理器中体现

第十章 Windows应用程序打包

第六章到第十一章的学习全部在我的作品密码管理器中体现

第十一章 实践项目-酒店管理系统

第六章到第十一章的学习全部在我的作品密码管理器中体现

个人设计:密码管理器

见单独文章密码管理器

书本勘误

这边记录我学习这本书籍WinForm程序设计与实践(廉龙颖)发现有错误的地方

  • “C#抛弃了C和C++的指针,不允许代码直接操作内存”【P11,LINE 1】

勘误:说法不完全正确,其实在不安全模式(unsafe)是可以直接使用指针操作内存的

  • “①当操作数的类型不同时,如”float result1 = 6.5 + 9;",C#编译系统会自动进行类型转换,先将第二个操作数转换位float类型,然后再进行加法运算,整个表达式的结果位float类型“【P70 LINE 2-4】

勘误:说法错误,首先6.5默认被系统保存为double类型的,系统会将9自动转换为double类型的9.0,然后进行运算,获得double类型的15.5,显然上述说法还有这个表达式本身就是错误的,详细说明见上面我的知识点分析隐式转换和显式转换的区别

  • 【P77 PICTURE】第77页插图中代码存在小问题

勘误:代码表达并非作者原意。需将源代码中类似Console.WriteLine("d & (c < 30),+(d & (c < 20)))的三行修改为类似Console.WriteLine("d & (c < 20),+(d & (c < 20)))的这样三行

  • ”自动转换一般在不同类型的数据进行混合运算时发生,当编译器能判断转换的类型,而且转换不会带来精度的损失时,C#语言编译器会自动进行转换“【P105 LINE 8-10】

勘误:上述说法不严谨,其中“只要转换不会带来精度损失时,C#编译器会自动转换”这种说法错误,比如说从long到double的隐式转换可能会丢失精准率,但绝不会丢失一个数量级。详细参见https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/builtin-types/numeric-conversions,但是这种说法在其它大多数情况下还是适用的

  • “可以有一个或多个catch块,但由于catch语句是依照它们出现的次序进行检查,进而确定哪一个catch处理异常,所以,如果某一catch语句指定的类型同它之前的catch语句指定的类型一致,或者由此类型派生而来,那么运行时就会发生错误。”【P113 LINE 3-6】

勘误:说法不严谨,It is possible to use more than one specific catch clause in the same try-catch statement. In this case, the order of the catch clauses is important because the catch clauses are examined in order. Catch the more specific exceptions before the less specific ones. The compiler produces an error if you order your catch blocks so that a later block can never be reached.参见:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/try-catch