第01次课:VS开发环境的使用和C#语法快速强化
- VS--》2017(2019)
- 数据库-->SQLServer2012
- 项目类型:(控制台-->主要是学习用;窗体程序(windows、c/s、桌面应用)、web程序、web服务程序)
- 项目的组成:
【1】解决方案:主要是用来管理我们添加的各种项目。可以对解决方案内部的所有项目,统一编译。
开发一个项目:这个项目和我们说的“项目”不是一个概念。
我们在解决方案中的项目理解:一个类库、一个可启动程序,都可以称为一个项目。
一般一个解决方案,对应我们一个真实的“项目”。
【2】项目包括:类库、可启动程序(一般就是编译后是exe文件的,能够独立运行的,比如windows程序、web程序)
【3】程序集(Assembly):一个类库、一个可启动程序...都是程序集。
【4】模块:(针对开发角度)模块通常是按照功能划分组成的各个类库或可启动项。
一个模块:可能是一个类库。也可能是多个类库!也可能包括可启动项。
【5】引用:是将不同的类库或者程序集,关联到一块。这样的话,可以实现对另一个程序集的访问。
注意:引用只能是单向的,不能双向。(A-->B 但是B是不能再引用A的) 引用的时候,注意高版本的程序集,可以引用低版本的程序集。反之不行。 A(4.6)-->B(4.6或以下) 反之,引用的程序集就会出现感叹号! 方法:通过右键添加引用,还有尤其是其他的更多扩展,可以通过nuget (尤其是.NET Core的使用中,只有这一种方式)
【6】发布:(可以直接使用,不需要专门的发布,如果你想做安装包)
开发阶段:我们一般使用dbug,因为通过dgbug我们可以断点调试程序。 发布阶段:我们一般在dbug没有任何问题的情况下,使用release发布。 不同之处:debug调试生成,必然会增加一些信息,大项目的话,生成的文件较大。 release发布后,一般直接可以使用。尤其是web开发的时候。会做特殊优化。 特别的,我们后面学习多线程的时候,我们讲过release会有bug!
请记住:解决方案中的每一个项目都有对应的debug和release文件夹。
当我们调试的或release的时候,每个对应的文件夹下面的文件都会自动更新。
文件分析:pdb与调试相关的。这个你不用管。会有dll文件,或exe文件
UI对应的debug里面--》项目的exe、BLL、DAL、Models、DBUtility...其他的第三方的引入的dll或者相关的其他文件...
BLL--》BLL、DAL、Models、DBUtility DAL--》DAL、Models、DBUtility DBUtility-》DBUtility Models--》Models
一个程序集在编译后生成的文件,首先包括他自己,其次包括他所引用的所有程序集。
当我们通过解决方案统一编译项目的时候,各个debug或release文件夹下面的文件都会被立即更新!
如果当我们程序调试过程中,由于你的调试的不正确等危险操作,导致这些文件不能立即更新,请自己手动删除
生成的所有文件即可。
【7】可启动项目的文件分析:
dll
exe:MisProject.exe MisProject.vshost.exe(VS开发阶段用的,最后开发完毕这个是没用的)
最后我们打包的时候,一般把项目可启动项目中release文件夹中的所有:dll文件、不带vshost的exe文件、
图片文件、其他的资源文件等,直接拷贝走就行了。
【8】第三方dll的引用:如果是.net开发的,直接引用。如果不是.net平台开发的(C\C++),请按照文档说明。
如果是.NET平台,不管你是用C#开发的,还是用VB.NET开发的,都可以自己引用。
- 项目生成分析
【1】dll和exe里面有什么?
注意:我们新建一个项目一般都会自动生成和项目名称一样的解决方案名称。
当然,我们可以自己单独创建一个空白解决方案,然后往里面添加需要的各种项目。
PS:当我们在一个项目中,引用其他项目,想使用里面的类,一定要在使用的地方,引入对应的命名空间。
通过使用ILDASM 工具查看,我们发现:
不管是C#开发的,还是VB.NET开发的,都会被首先编译成IL(微软中间语言)
所以,C# 可以调用VB.NET开发的模块,反之亦然。 注意:C# 和 VB语法不一样,但是IL中是一样的,为什么?因为有一个叫做CLS这样一个“语法翻译官”。 C#和 VB 数据类型不同,但是IL中是一样的,为什么呢?因为有一个叫做CTS的翻译官。 两种不同的开发语言,数据类型看似不同,但是最后编译的IL都是.NET数据类型。 你好!不同国家翻译是不一样。 入乡随俗!
【2】程序执行的过程
写代码-->调试后编译成IL-->编译程机器语言(其实是一个非常复杂的过程)
- 命名空间的使用
作用:就是用来对各种类进行管理。也就是一个类一定要归属于某一个特定的命名空间。
一个命名空间可以包括若干类
namespace MyLibraryCSharp { public class TestB { public string Study() { return "我们正在学习C#开发技术!"; } } }
类必须在某一个命名空间下面。不能脱离命名空间!
方法必须放到类里面。
命名空间的使用注意:
当我们引入一个程序集的时候,必须要同时添加using 命名空间,才能使用这个命名空间下面的类。
不同的命名空间下面的类是允许同名的。
当我们在一个类文件中,引入了不同的命名空间,但是如果这些命名空间中有相同的类名,我们必须通过
完全限定名来使用,也就是:命名空间.类 这种方式,否则就会出现错误提示。
特别注意:类库项目是不能直接启动的。
第02次课:C#核心语法汇总强化与常见问题分析
1、关于变量定义
【1】变量类型:
局部变量(在方法内部使用的变量,只能给这个方法自己使用)(随着方法执行完毕而销毁)
成员变量(方法外面、类的内部定义的,可以被类的内部其他方法使用) 随着对象销毁而销毁
全局变量(一般是public static修饰的)也就是说,其他任何类都可以通过《类名.全局变量名》
【2】变量使用
注意问题:不能超出作用范围
必须要先赋值,因为我们使用的变量,要求必须有一个明确值。
在字符串中:“”和string.Empty是一样的。
如果我们定义一个字符串变量,什么都没赋值,默认是null
string a;
string b=null;
null=string.empty=""? 对吗?0等没有吗? 1-1=0
以上虽然a和b都是null,但是有区别:null虽然是空值,但是这个是有赋值的过程。而a是没有赋值。
string c=a+"您好"; int lenght=a.Length;
【3】变量命名
在C#规范里面,变量命名是遵循的命名法是:camel命名法(首字母小写:studentName)
2、关于常量:(做上位机的学员,经常使用)
【1】使用:固定不变的值,可以定义为常量,在数学模型中经常使用。
视觉:角度==>弧度
【2】命名:一般都是大写(为了很好区分)
3、枚举:表示一组有限值
【1】为什么要用枚举?
我们知道,变量可以根据需要定义,来表示我们使用的数据。但是对于我们经常使用的,并且固定的几个值 如果我们都定义成变量,使用和不方便,为什么?因为不好记忆!所以,把这几个有限的值,定义成枚举后 通过VS智能提示,可以轻松的让我们使用。 MessageBox CommandType .....
【2】枚举定义
第一,必须是有限个(不能太多,一般2-5个,6-10,11-20个) 第二,枚举表示的是整数。可以相互转换。 第三,枚举我们一般都定义到类外面。因为可以被其他的类使用。
(3)、枚举的定义语法
在没有枚举类型时定义常量常见的方式
public class DayDemo
{
public static final int MONDAY =1;
public static final int TUESDAY=2;
public static final int WEDNESDAY=3;
public static final int THURSDAY=4;
public static final int FRIDAY=5;
public static final int SATURDAY=6;
public static final int SUNDAY=7;
}
上述的常量定义常量的方式称为int枚举模式,这样的定义方式并没有什么错,但它存在许多不足,如在类型安全和使用方便性上并没有多少好处,如果存在定义int值相同的变量,容易混淆,因此这种方式在枚举出现后并不提倡,现在我们利用枚举类型来重新定义上述的常量,定义周一到周日的常量
//枚举类型,使用关键字enum
enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
相当简洁,在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。
枚举类型Day中分别定义了从周一到周日的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天。
以上是写法,写好后该如何使用呢?如下:
public class EnumDemo { public static void main(String[] args){ //直接引用 Day day =Day.MONDAY;
就像上述代码那样,直接引用枚举的值即可,这便是枚举类型的最简单模型。
4、关于运算符
【1】常见运算符(自己看预科)
【2】取模运算符(余数)%(在数据分页等都会用到) / 取商
【3】变量自增 num++ 和++mun num-- 和 --num 单独从执行的角度来讲是一样。
int num=10;
Console.WriteLine(num++); 输出:
Console.WriteLine(num);
Console.WriteLine(++num);
输出: Console.WriteLine(num);
for循环中经常用i++
5、关于数据类型转换(我今天讲的和前面录播讲的,都是一般情况,对于高精度运算)
自动类型转换:一般是值类型之间(精度小的,转换成精度大的!) 引用类型到object类型是可以直接转换的。(但是尽量避免,我们在这个地方也不举例) 强制类型转换:如果自动类型转换不了,必须使用强制转换! (int)reader["列名"] 【1】值类型之间 【2】字符串和值类型之间(注意这个字符串必须是值类型的有效表示形式:“20.5” “20.A”不可以) 【3】万能转换器(如果你是一个初学者,前面两种方式你可能记不住,如果没有特殊要求,你都可以使用万能转换器) 特别注意::小数部分等于0.5的时候,看整数部分:奇进、偶不进
*【C#核心语法强化(二)】
6、分支结构
为什么要使用分支结构? 当我们处理的业务,需要有不同情况,这时候就得单独处理,就用到分支结构。
分支结构,是根据不同的条件,执行不同的业务逻辑。
【1】条件分支结构:if 、 if - else 、if -else if-- else ifelse ifelse ifelse ifelse if-else
特别注意:只要是互斥类型判断,就不要使用多个独立的if...
if 条件分支一般在任何情况下都可以做条件判断使用。(范围和具体值都行)
【2】常量分支结构:switch case (等值判断)
7、循环结构
【1】循环(for、foreach、while、do while)
for:循环次数固定,不仅是是获取循环内容,可能也会关注循环次序(1、2、3、4、5、....)
foreach:不关心循环次数,特别注重循环内容(也就是元素)、无条件执行。
whlie ():不关心循环次数,特别注重的是循环条件的满足,同时循环过程与循环条件没有任何关系。 foreach( var 元素 in 集合) { 元素使用。 } 总结:foreach循环是便利容器中的元素 while(循环条件) { //自定义的业务内容... }
总结:根据条件是否满足,循环执行某些自定义业务。
while中的循环跳出:break、continue区别
【2】 break、continue和return
break:在循环中和分支结构中使用,表示跳出。
continue:一定是在循环中使用,本次循环没有结束,就开始下一个循环。
return:表示无条件跳出,通常是我们普通的方法中使用,表示后面的代码不执行。
如果return 后面有数据,表示这个方法执行完毕后,返回数据。
if (this.txtStudentName.Text.Trim().Length == 0) { MessageBox.Show("学生姓名不能为空!", "提示信息"); this.txtStudentName.Focus(); return; } if (this.txtCardNo.Text.Trim().Length == 0) { MessageBox.Show("考勤卡号不能为空!", "提示信息"); this.txtCardNo.Focus(); return; }
*【C#核心语法强化(三)】
8、字符串专题
【1】掌握常用的几种方法:
特别的:字符串比较方式(a==b a.Equals(b)) ==在我们C#中可以比较《字符串》和《值类型》数据! Equals()虚方法也是一样的。 但是以上两种不能比较对象! 在Java中,==是不能直接比较字符串。
【2】字符串长度的获取:
Lenght:属性 如果我们获取集合元素个数:Count属性 如果是数据库中,你会看到Len()函数
【3】StringBuilder字符串高效处理
9.数组
【1】在什么情况下使用数组?固定元素个数的统一存储。
在我们一般开发中, 使用数组的时候很少了。特别的,上位机开发的可能用的多。 byte[]
【2】字符串分割和数组使用配合
分割后,都是放到数组中的。特别对于一些文本的数据处理要用。
10.其他的:
ref(不建议使用) out
private Add(ref int a ,int b)
{
}
03:WinForm入门和企业项目标准UI设计
Q1:CLR CLS CTS关系搞不清楚
A:以上三个内容其实是不同的程序“模块”。就好比我们说的电脑的几个模块:CPU、内存、硬盘、主板。
CLR=CLS+CTS CLR:公共语言运行时(虚拟机)主要是用来将我们写的代码编译成微软的中间语言IL。 CLS:在编译中,我们知道C#写的代码,和VB写的代码,在语法上是不同的。当编译成IL的时候,必须对语法进行 标准化处理。 CTS:在编译中,我们知道C#写的代码,和VB写的代码,在数据类型上是不同的,当编译成IL的时候,必须对数据 类型标准化处理。
Q2:如果引用的程序集不在当前目录下,在哪里设置路径?
A2:当我们引用第三方的类库的时候,我一般是复制到项目的启动目录下面。
基础语法-->做练习巩固(枯燥)-->UI设计(windows做一个快速入门)
1、项目UI设计
【1】C/S项目:首选是Winform原生控件(简单实用,但是效果不爽)(直接拖放控件)
第三方控件:dev.... 、免费的控件库 WPF(界面设计上是最漂亮的....但是,你必须付出努力才行!学习成本较高) 自己动手设计UI:今天要讲解的,能在以上各种选择中,各方面都比较折中的方案。 局限性:就是屏幕自适应能力达不到要求。
【2】B/S项目:HTML5原生设计(CSS+DIV)太麻烦,但是性能非常高!
jQueryUI、Extjs、EasyUI..(早期) BootStrap框架(响应式设计,非常流行,我们的全栈课程中,有讲解) LayUI框架(这个也是我们全栈课程重点讲解的,学习简单,上手快,效果又好,一般在后台应用多) ...
2、常见的项目的类型
【1】控制台程序(主要是学习使用)
【2】WinForm程序(windows程序、桌面程序、C/S程序,客户端程序...)
【3】Web程序和分布式服务程序(asp.net-webform、asp.net-mvc、webservice、webapi、wcf...)
3、不同项目部署差异
【1】WinForm程序:我们.NET开发的程序,可以直接把生成的文件拷出来就能用了。根本不需要单独的安装包,
我们打包程序,仅仅是把程序模块集中到一起而已,对注册表没有任何的修改。 部署:第一、如果有数据库,数据库会在专门的服务器上。 第二、应用程序,在独立的客户端电脑上。(通常会有多个客户端) 第三、项目升级服务器。
【2】Web程序: 我们开发的程序,通常只有一个服务端,通过IIS部署到服务上,客户端其实就是浏览器。
部署:第一、数据库通常是独立的服务器。
第二、应用程序,在独立的服务器上。
4、WinForm程序设计
【1】项目的结构
第一、Properties文件夹:
AssemblyInfo:程序集信息的配置类(通常项目正式发布后,存储项目的版本、版权、项目相关其他信息)
第二、窗体文件Form1
包括:Form1.cs
Form1.Desinger.cs
两个类的定义:
public partial class Form1 : Form { public Form1() { InitializeComponent(); } } partial class Form1 { //其他内容省略... }
结论:两个类的类名称是完全一样的!
public partial class A { } public partial class A { }
【2】部分类(partial)
当两个类在同一个命名空间下面,类名称一样,但是使用partial关键字后,就有意思了。也就意味着这两个类
看着是两个类,其实是一个类的不同部分。英语中part1 part2...
目的:当一个类的内容太多的时候,我们编写和查找都容易出现不便。为了更好的管理类的代码,在VS2005的时候
出现了部分类。
public partial class Form1 : Form { public Form1() { InitializeComponent(); } }
这个类里面的代码,主要是我们自己写的。
下面是Desinger文件中的代码
partial class Form1 { //其他内容省略... }
这里面主要是VS自动生成的代码。
好处:可以不同的人编写同一个类,当然目的是更好的对代码进行分类管理。最后还能编译成一个类。
建议:不要轻易添加窗体的部分类!我们开发一个建议:当我们发现这个类的内容太多的时候,请大家按照OOP原则
将类的内容再次细分,封装到其他类中。
【3】常用控件
按钮控件:当我们在窗体中添加一个Button后,其实是VS自动的根据Button类帮我们创建一个按钮对象,并自动的
设置了相关的属性:
private System.Windows.Forms.Button button1; 这个说明按钮是窗体对象里面的一个成员变量(对象)
当我们通过属性窗口修改完控件的属性后,后台代码会自动的修改:
// // btnTest // this.btnTest.Location = new System.Drawing.Point(21, 26); this.btnTest.Name = "btnTest"; this.btnTest.Size = new System.Drawing.Size(114, 51); this.btnTest.TabIndex = 0; this.btnTest.Text = "启动按钮"; this.btnTest.UseVisualStyleBackColor = true;
建议:不要随便的手动从后台修改。但是,我们如果需要动态的创建控件,我们通常会自己先添加一个控件,设置属性后
把这些代码复制到我们的相关的方法中,通过动态修改属性,来实现控件的动态控制。(后面我们讲解OOP案例的时候)
我们还发现在Form1窗体的设置中多了两个内容:
// // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(611, 427); this.Controls.Add(this.comboBox1); this.Controls.Add(this.btnTest); this.Name = "Form1"; this.Text = "Form1"; this.ResumeLayout(false);
就是这两句话:
this.Controls.Add(this.comboBox1); this.Controls.Add(this.btnTest);
以上说明,我们窗体本身就是一个容器
Controls的位置:Form--》ContainerControl--》ScrollableControl--》Control:里面的Controls集合属性
Controls集合的类型:ControlCollection -->
将指定的控件添加到控件集合中。public virtual void Add(Control value);
想理解这个Add方法,我们再看看控件Button类是怎么定义的?
public class Button : ButtonBase, IButtonControl
public abstract class ButtonBase : Control
结论:Button--》ButtonBase--》Control 那就说明Button是Control类的一个子类(孙类)
public virtual void Add(Control value)这个方法中的参数定义Control是父类类型。
按照OOP多态特性:一个方法的参数如果是父类类型,那么实际传递的时候,我们要传递他的子类对象。
this.Controls.Add(this.btnTest); 这个不正是我们后面讲的多态吗?
最后还要记住:窗体上所有的控件,都要添加到Controls集合中。窗体本身就是容器。
如果我们在窗体中添加其他的容器,比如panel、groupbox等等,这些首先被添加到Form的Controls集合中。
但是如果我们在Panel等这些容器中添加控件的时候,控件会加到它所在的第一级容器中。
// // groupBox1 // this.groupBox1.Controls.Add(this.button1); this.groupBox1.Location = new System.Drawing.Point(312, 87); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(200, 100); this.groupBox1.TabIndex = 2; this.groupBox1.TabStop = false; this.groupBox1.Text = "groupBox1";
关于Controls我们后面会经常使用。
==》启动窗体
static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } }
在这里是程序的入口,我们发现 Application.Run(new Form1());这句话告诉我们Form1窗体是作为项目的启动窗体
启动窗体就意味着项目的主线程运行,当我们关闭Form1窗体的时候,整个项目会退出的。
我们可以根据需要修改这个启动窗体。
我们先暂时修改为FrmAdminLogin
5、无边框窗体设计
思想:我在Iphone5出现的时候,里面的图标都实现了扁平化设计。后面很多手机都跟风。并且流行。
思考:PC端能否这样呢?
当然没问题!
方法:
第一、窗体边框去掉。然后自己设计“边框”和颜色。
第二、窗体大小,必须提前设计好。
第三、控件要扁平化处理
扁平化的好处:不管你是在win7、win8、win10上显示的效果完全一样。
04:企业级UI设计扩展和部分作业讲解
一、项目主窗体设计
二、子窗体设计和嵌入
三、答疑
四、选择题讲解
第05次课:C#面向对象三大核心强化(OOP的概念理解、对象的属性、对象的方法)
之前:C#语法-->Winform开发基础-->UI设计
现在:OOP(面向对象编程)
一、面向对象的快速理解
对比面向过程的特点:。。。。。
- 为什么要学习OOP?
A:在C#、java等这些完全面向对象编程语言的世界里,我们必须要掌握面向对象编程的方法!
- 什么是OOP?
A:首先,我们使用面向对象编程语言写程序的时候,必须以“对象”为中心进行思考、设计。也就是要把我们完成的各种
任务,以对象为基础单元进行划分(也就是类的形式)。
PS:最早是面向过程,因为我们的程序规模非常小。我们都是用各种函数,按照一定的逻辑进行执行和中断服务子程序的执行。
但是随着项目规模的大,我们以前的方式,就无法满足系统的需求。必须对系统进行“类的划分”,以类为单元封装对应
数据和功能。
最简单的:汽车生产厂商生产汽车的时候就是典型的,以对象为中心。
好处:分工非常明确、专注某一部分、扩展方便...
- 怎么学习OOP?
A:我们学习OOP,主要就是学习OOP的各种原则、方法、技巧、模式、经验。
原则:六大原则(原则是告诉我们类的边界是如何界定的、关系如何确定、扩展是如何实现的、效率是如何提升的)
系统:1、2、3、4、5....功能点。请问,如何把这些功能点划分到类中?(对象职责明确原则)
方法:具体实现的方法,没有一个所谓的总结,只有你跟着老师不断的联系,体会。(属性、行为如何封装)
技巧:老师给大家的慢慢传授的。
模式:一般指代设计模式。也就是说我们按照OOP方法开发程序的时候,还会遇到各种问题,这些问题有时候具有通用性,
我们的前辈工程师,针对这些问题,做了方案的整理,我可以直接借鉴。模式是OOP过程中解决特点问题的一些技巧。
经验:通过做项目,形成自己的知识体系和认知。(也就是大家学好后,应该有创新能力)
- OOP学习中的重点和难点是什么?
A1:三大特性:封装、继承、多态
封装:你天天在用,实时在用。
继承:其实在.NET底层用的非常多,我们主要是理解。开发中用的较少(但是非常重要,较少不代表不用)
多态:处处在用。(尤其是学习高级编程的时候,当我们分析源代码,你会发现,多态无处不在)
A2:对象的设计和封装。(但是大家不用担心,纯面向对象的理解,我们课程中讲的例子,足够难)
- 从哪里开始学习OOP?
A:从一个类的封装和一个对象的使用开始!
-->类的概念、类的组成、对象的使用!
总结:一个项目就是由不同的类组成的。
二、面向对象的初步实践
1、通过计算机的例子,我们得出结论:如果不使用面向对象的方式,会发现,随着后面功能的不断扩展和优化,
我们的程序代码,需要不断的修改和增加。到一定规模后,会非常乱!
好的方式:
第一、UI进行美化设计。
第二、以对象为中心进行抽象。(就是要思考一下,我们这个项目有哪些对象参与,这些对象应该封装什么数据和行为)
当我们把一些数据和行为独立封装后,必须要考虑数据的传递问题。
数据要封装成属性:从外面能够赋值。其实数据传递有两种方式:一种是通过属性,一种是通过方法参数传递。
属性:通常我们可以提前保存数据。
参数:调用时候随时传递数据。
思考现实生活的例子:我们去饭店吃饭,可以当时结账。也可以办理会员,会员就是提前充值。
类是封装属性和行为。当我们使用类中的属性和行为的时候,通常都是以这个类的对象形式。
new一个类,就是创建一个对象,这个对象需要的数据存储空间等都会开辟对应的内存空间。当我们使用完以后这个对象会被
GC清理。所以C#完全面向对象编程和C++等半OOP最大的区别,就是对象的清理不需要我们个人干预。
补充:windows的事件
1.事件组成:事件委托(后面去讲) + 事件方法
//清除:事件方法(响应用户操作的) private void btn_Clear_Click(object sender, EventArgs e) { this.lblResult.Text = ""; } //事件委托 this.btn_Clear.Click += new System.EventHandler(this.btn_Clear_Click);
注意:当我们删除事件委托的时候,事件方法不会在用户操作的时候,产生响应。
当我们删除事件的方法时,没有删除对应的事件委托。则会报错。
类的组成:属性(数据结构)+方法(行为)
三、面向对象中的属性研究
1、为什么要使用属性?
属性:属性表示对象的静态特征(与数据相关的)
对象封装的时候,一般都要封装相关的数据,数据封装的方法?属性
为了更好的理解属性:我们首先使用字段封装数据。
字段:就是定义在方法外面的变量。(成员变量)
当我们把字段设置为public的时候,我们就可以通过《对象.字段名》方式访问字段。
不足:因为字段可以定义成我们能使用的任意数据类型。
但是,我们在使用中,比如int类型,定义年龄,我们不希望年龄是负数,但是int类型又允许负数,所以,对于负数年龄等非法数据不能够很好的过滤。
解决方法:我们可以通过属性封装字段。把字段变成私有的。通过公有的属性去操作这个字段。
通过添加公有的属性:我们发现,属性既可以直接操作单个的字段,也可以操作其他的属性,和行为的调用。
2、字段和属性怎么选择?
标准:首先你要看,是内部使用,还是外部使用。
属性:可以内部使用,也可以外部使用。(但是如果我们定义一个变量,不需要外部使用。就没要使用属性)
字段:只有内部使用。(外部使用,很少)
属性本身:只是外部访问内部数据的一个接口,它本身不保存数据。数据还是放到私有字段里面。
属性也可以不操作私有的数据。
四、对象的方法研究
方法:主要是封装功能点的,我们开发软件,主要是写各种方法。
1、方法定义,要特别的注意返回值和参数。
返回值:就是这个方法执行完以后,给调用者返回什么样的数据!(基础数据类型的数据、实体对象、集合对象)
基础数据类型的数据(普通的基础类型外,还有后面针对web服务开发的json数据等)
参数:就是我们调用这个方法,需要的数据。
2、实例方法和静态方法
【1】没有参数、没有返回值:通常用于初始化的操作。
private void InitializeComponent(){}
【2】没有返回值,有参数:通常我们完成一个任务,就结束了。(这个我们自己定义的时候,通常是定时任务等)
最多见到的就是事件方法。
Console.WriteLine()
【3】有返回值、没有参数 :用的不多。一般就是用于一些固定的任务。比如,我们每天晚上11点,系统要做一些数据分析...
【4】有返回值,有参数:用的最多。
【5】参数带默认值的方法:前面带参数情况的特殊使用(用的非常多)比如我们后面讲ADO.NET相关方法的时候,经常用。
使用参数默认值好处:配合方法重载非常方便。
【6】命名参数:这个是对多个参数传递的时候,可以打破顺序,让调用更清晰。可以适当使用。(web,mvc的路由参数)
使用参数命名好处,可以根据参数名称去对应参数值。
【7】静态方法:一般用的很少,除非我们访问非常频繁,但是又不容易产生并发的情况下,可以适当使用。或者程序运行的一开始我们就希望这个方法存在,可以使用静态方法。记住静态方法不收GC(垃圾回收机制)约束,只有程序关闭,内存才是方法。
静态成员也是一样的。
- 构造方法
【1】作用:在对象创建过程中,用于初始化对象的。
【2】使用:必须和类名一样,并且不能有返回值,当我们new的时候,new关键字后面的其实就是一个构造方法。
也就是说,我们创建一个对象,就是通过调用它的构造方法完成。
【3】可以初始化哪些内容?
无参数的构造方法:通常是初始化固定的内容。(比如固定的成员变量数据、调用配置文件数等)
有参数的构造方法:主要是根据参数初始化对象的成员,或者完成其他逻辑的调用(典型的,我们在学员管理系统中,比如用户选择一行学员信息,当我们查询学员详细的时候,我们可以把当前行学员的ID传到一个新的窗体中,然后新的窗体接收到,再去查询数据库,然后展示)。
【4】构造方法之间的调用:
: this
后面我们学习继承的时候,还会用到:base()
this关键字:
【1】用到构造方法内部调用。
【2】通过this关键字可以调用成员变量。(也可以不用,但是)
【3】如果一个方法中的参数名称和成员变量名称一样,通过this可以很好的区分。
因为如果我们不带着this,当我们使用一个成员变量的时候,会采用就近原则。
this不能调用静态成员。(this表示当前类的实例)
——————————————————————————————————————————————————
作业:【1】UI设计的后半部分(其他没有设计的窗体,自己完成)
【2】对象属性和方法的基本功练习。
预告:OOP还有一个重要内容,对象的“存储”。我们后面讲一个OOP综合案例,把前面的内容都能串联起来。
预习:OOP的彩票选号器。
学员问题:有了GC,析构函数还有用吗?
GC:是负责回收对象的(释放对象占用的各种资源)
析构函数:和构造函数是对应的。构造函数是对象创建的时候调用的。析构函数是GC回收的时候使用的。
也就说如果我们希望GC在回收对象的时候,做一些事情,可以使用析构函数。
第06次课:基于OOP方法设计双色球选号器
基础:C#语法-->C#面向对象基础(掌握基础的类与对象的使用方法)
预科:完整的讲了一个体育彩票选号器(给大家的都是经过优化后的)
非常重要的内容:多个对象如何处理呢?多对象封装使用集合!
泛型:泛型其实是一种程序特性,我们经常使用的有泛型集合、泛型方法、泛型类、泛型委托
T:Type类型。
ArrayList就是一个能够存放任何数据的动态“数组”。
ArrayList list=new ArrayList();
list.Add(10);
list.Add("你好");
我们希望一个集合能存储的数据元素的类型是固定的,而这个集合又能存储各种类型的数据。只不过每次只能存储一种类型。
所以,当我们具体要存储一种数据类型的时候,就把这个T换成具体数据类型即可。
List scoreList=new List();
List courseList=new List();
添加元素:
【1】如果是一个元素,直接使用Add(元素);
【2】如果是一组元素,比如一个数组,直接使用AddRange(数组)方法。(非常多)
其他的:删除、查找方法
还有,更多的linq查询方法,我们后面都要学习....
现阶段:大家只要知道我们处理的任何对象,一般都会保存到List集合中,然后遍历集合、必要的时候,删除元素。
key value
Dictionary 基于键值对的字典集合
("张三",“我来自深圳”)
("李四",“我来自北京”)
这个字典集合的好处,就在于,我们可以根据key快速的得到需要的value
一、双色球选号器UI设计
1、UI总体设计(自己完成)
2、UI中的红色球和蓝色球动态生成和位置确定的方法。
3、根据以上分析,动态的生成红色球;
4、按照红色球的生成方法,生成蓝色球
5、其他的事件和准备工作。
总结:
【1】控件动态生成的方法
【2】控件事件的关联方法
第07次课:基于OOP方法设计双色球选号器
基础:C#语法-->C#面向对象基础(掌握基础的类与对象的使用方法)
预科:完整的讲了一个体育彩票选号器(给大家的都是经过优化后的)
非常重要的内容:多个对象如何处理呢?多对象封装使用集合!
———————————————————————————————————————————————————
类似数组的集合:List泛型
泛型:泛型其实是一种程序特性,我们经常使用的有泛型集合、泛型方法、泛型类、泛型委托
T:Type类型。
ArrayList就是一个能够存放任何数据的动态“数组”。
ArrayList list=new ArrayList();
list.Add(10);
list.Add("你好");
我们希望一个集合能存储的数据元素的类型是固定的,而这个集合又能存储各种类型的数据。只不过每次只能存储一种类型。
所以,当我们具体要存储一种数据类型的时候,就把这个T换成具体数据类型即可。
List scoreList=new List();
List courseList=new List();
添加元素:
【1】如果是一个元素,直接使用Add(元素);
【2】如果是一组元素,比如一个数组,直接使用AddRange(数组)方法。(非常多)
其他的:删除、查找方法
还有,更多的linq查询方法,我们后面都要学习....
现阶段:大家只要知道我们处理的任何对象,一般都会保存到List集合中,然后遍历集合、必要的时候,删除元素。
key value
Dictionary 基于键值对的字典集合
("张三",“我来自深圳”)
("李四",“我来自北京”)
这个字典集合的好处,就在于,我们可以根据key快速的得到需要的value
一、双色球选号器UI设计
1、UI总体设计(自己完成)
2、UI中的红色球和蓝色球动态生成和位置确定的方法。
3、根据以上分析,动态的生成红色球;
4、按照红色球的生成方法,生成蓝色球
5、其他的事件和准备工作。
总结:
【1】控件动态生成的方法
【2】控件事件的关联方法
【3】两种集合的重要应用
本次主题:基于OOP方法实现双色球选号器
二、基于OOP方法设计选号器的所有功能
- 我们首先要思考如何去设计项目需要的对象?
思考:项目通常需要什么类型的类(对象)?
【1】边界对象(UI:用于和用户交互的这些界面对象、用于输入输出的各种对象:打印...)
【2】数据对象:(实体对象--》数据库一条数据)
当前项目中应该处理哪些数据? 双色球彩票号码(DobuleChromosphere) 当我们找到对象后,我们就要设计对象,请问设计什么?A:属性、方法! 属性:红色球(多个) 蓝色球(多个) 投注方式 金额 方法:(构造方法、实例方法、静态方法) 构造方法中-->根据投注方式,把金额算出来(需要你明白投注的算法公式) ....
【3】业务对象:(主要用来封装项目功能的类)
当前项目中应该找到哪些业务对象? 选号器对象(Selector): 属性:红色球池(集合)、蓝色球池(集合)、选中的号码(集合) 方法:随机选号、(打印、保存到数据库)
以上分析和设计的好处是:让大家重复体会面向对象中“高内聚、低耦合”。
- 根据以上的分析,实现两个类的具体编写....
- 在UI中使用选号器对象完成选号功能...
作业:根据UI实现其他的机选、提交购买等扩展内容...
第08次课:OOP六大原则与OOP案例强化练习(针对作业的)
本次目的:彻底学会类的设计,对象的使用
设计类就是完成属性和方法的设计!
属性:类需要封装哪些数据!
方法:类要实现哪些功能(任务)!
考虑到每个人的思维角度不一样,使用的方法有差别,最后设计的属性和方法也有差异。
只要道理上讲得通,就是合理的!
这个道理是什么?今天我们学的就是这个“道理”!
A:道理就是我们要掌握的各种面向对象的原则!
引出:学习面向对象就是学习一系列的原则、方法、技巧、经验、设计模式等!后面高级的技能点,相对来说是容易!
一、OOP原则
- 单一职责(对象职责明确原则):SRP,一个对象所要完成的任务应该是明确的。也就是我们不要设计万能类!
不好的做法:有的人设计窗体类或者其他的功能类,一个类中有1千两千行代码!这么多的代码,一定能细分的!
双色球彩票选号器!涉及到类:选号器、双色球类--》业务类、数据类 窗体--》边界类
后续扩展:
职责问题:窗体类--》获取用户操作行为和数据、展示相关数据
双色球类--》封装了我们购买的彩票数据 选号器类--》主要封装了用过请求的各种行为(比如:选号、存储、打印....)
【应用】:主要影响的是我们思考问题的角度和内容。
- 开闭原则(开放扩展,封闭修改原则):OCP:就是说我们设计完一个项目后,用户的需求的经常是变化的,如果你的项目
设计的非常好,当用户需求变化的时候,你的项目代码修改的非常少,甚至没有修改。只有增加的模块或者类(扩展)。
生活举例:USB接口,可以扩展很多USB设备。
这点在我们开发中处处在用,我们后面学习的各种设计模式、设计方法,很多都是围绕扩展来展开的。
【应用】:主要影响的是我们程序总体的设计、或者某些模块的设计。说白了就是把各种模式、方法你学会后一个运用。
比如:首先会讲到简单工厂!
Selector mySelector=new Selector();也就是说当我们new一个对象的时候,这个对象在编程阶段是明确的。
什么时候用这种方式呢?如果你要使用的对象是不变的,那么尽快放开使用。
但是,如果我们程序要使用的这个对象,并不是唯一确定Selector本身,可能还有其他的对象选择。这时候,我们就不能直接new
怎么办?我们就要考虑把对象创建过程封装!交给第三者完成。这个第三者可能是方法、类、或某些模块、第三方的库等。
Selector mySelector=【工厂方法或模块】;这样的话,我在程序编写阶段是不能确定这个对象的,在程序运行阶段是确定。
结果:工厂方法内部如何变化,对我们调用者没有影响。
回到主题:用户需求的变化被封装到了工厂方法或模块中,这样的话,我们程序本身在需求变化的时候不修改,这种做法就遵循了
开放封闭原则。
这就解释了,后面我们为什么要学习各种设计模式和设计方法,就是这个道理。
- 里氏替换原则(这个就是我们即将讲到的继承中使用的):LSP,这个在我们讲完继承后,你会明白。也就是说父类出现的地方
可以使用子类替换。(这种通用性,可以延续到接口)(也就说,接口出现的地方,可以用接口实现类替换)
这个也多态的基础!如果你不理解这个原则,C#编程你等于没学!
Class A{}
Class B:A {}
Class C:A{}
【1】如果A是普通类,我们可以A a=new A();
【2】如果A是抽象类,我们就得用 A a=new B(); 也就是说抽象类作为变量的类型,后面必须指向的是子类对象。
A a=new C(); A a=【工厂方法】返回一个B或C的对象!
【答疑】obejct类,能不能所有的控件都基于以上的方式使用。object o=new Button();
提示:学东西不能生搬硬套!
录播:是希望你能在掌握面向对象基础的情况下,更多的掌握技能点。积累项目开发经验提高熟练度。编程的方法。
直播:目的不仅是重点、难点、扩展知识的讲解。更多的是侧重:编程的艺术。
注意:前面,如果我们需要扩展,考虑扩展,可以使用我们上面的方法!但是你不要为了扩展而扩展。
object o=new Button();
- 迪米特法则(LoD):最小耦合原则(最小知道原则、最小知识原则),主要完成的是解耦。
思考:为什么要解耦?其实我们讲解各种原则是相互依赖,相互影响的,为什么难学,难用。
解耦:其实开闭原则,也是解耦的实现方法。所有的扩展,模式,都是在围绕解耦。
【应用】:后面我们给项目分层,分模块,都是在解耦。
- 接口隔离原则(接口最小化原则):
A模块--和B模块关联的时候,接口非常多,这种做法就不行!
接口是一个广义概念!
请大家回顾:你们学习我讲方法的时候,特别提醒了各位,方法参数定义的时候,一般不要超过4、5个参数。
如果方法参数太多,调用的时候非常麻烦!
其实我说这个话的依据:就是这个原则!解决方法:实体类,用对象作为参数!
广义:比如我们封装一个模块的功能,首先设计接口,设计接口的时候,你也不要设计的过多。
接口设计太多,意味着对象很多,方法很多。我们经常说一句话:鸡多了不下蛋,人多了打瞎乱!
- 依赖倒置原则(DIP):
盖楼:第一层、第二层、第三层........顶层
请问:你听过先盖顶层.....?
编程可能就是相反的方式!为什么要倒置?也就是下层依赖上层,或者“细节”依赖“抽象”。
目的:第一,可能是扩展的需要。第二,团队协作的需要。
抽象的类型1:可以通过抽象类和抽象方法
///
/// 父类 : 抽象层
///
abstract class Person
{
public int ID { get; set; }
public string Name { get; set; }
public void Study() { } //抽象方法 public abstract void Having(); } /// <summary> /// 男人继承自人类(通过继承可以直接使用父类的属性和方法) /// 细节层 /// </summary> class Man : Person { public string CusAttrFromMan { get; set; } //一般情况必须要重写抽象方法... public override void Having() { throw new NotImplementedException(); } } class Women : Person { public string CusAttriFromWomen { get; set; } public override void Having() { throw new NotImplementedException(); } }
//扩展:增加其他的细节
抽象的类型2:通过接口类
团队协作:我们的架构师或者项目负责人,首先搭建项目框架,根据业务编写各种接口,把项目的核心业务都组件完。
然后,具体的开发者,可以根据这个框架和接口来完成具体的细节开发。
如果我们开发中项目非常大,必须把这种原则提醒的非常明显,才能够显示你的项目水平。
可能会有各种框架的选择,或者自己的设计的框架。
后面会接触到的名词:面向接口编程(面向抽象编程)、面向服务编程、面向切面编程....
webservice webapi
面向对象编程-->面向接口编程(面向抽象编程)、面向服务编程、面向切面编程....
举例:国家根本大法;宪法!-->各种类型的其他法律法规-->地方法规。
学员总结:以中心思想分布式学习!
后面学习:更多的是实践----->再理论---->再实践.....
请记住:一定的理论没有,你是无法实践好的!
第09次课:C#中的继承多态机制分析与使用
你学C#不懂多态,你就不懂面向对象编程。今天的内容依然具有通用性,比如你后面学习java,和老师讲的是完全一样的。
一、继承基础
- 继承概念:从现实生活中理解继承,说我们生活中的继承主要是财产,我们程序中的继承,主要是代码(属性、方法)
- 为什么要使用继承?通过创建一个windows窗体程序,观察两个窗体,都继承自Form
A:代码复用
- 继承在使用中,有什么要求和特点呢?
特点:继承具有传递性! A-->B-->C A会具有B和C的相关属性和方法,也就是C会把允许继承的内容,传递给他的下一级。
要求:继承虽然可以有很多层,但是继承具有单根性!也就是说一个类A可以继承自B,但是A不能同时继承C....
C++中是允许多重继承的,但是C#和java是不允许的。
A-->B,C 这个是不允许的。
因为多重继承如果不注意很容易“混乱”。
我们刚才观察Form的继承关系,你会看到:
public class Control : Component, IDropTarget, ISynchronizeInvoke, IWin32Window, IArrangedElement, IComponent, IDisposable, IBindableComponent
我们发现,Component后面的全部是以I开头的类,其实这些全部是接口。
也就是说,一个类可以同时继承一个父类,同时再实现多个接口。
4.继承的基本使用
【1】要求:当我们分析某些类的时候,如果这些类之间具有相同的属性和方法,我们可以考虑继承,但是使用继承的时候,要
特别注意,我们就有继承关系的类之间,一定是“同类”。
分析:创建学生类,讲师类
姓名、身份证、出生日期 ... 工作.... 以上应该放到父类Person中。
接着,我们创建两个子类:Student、Teacher 继承后,添加自己的特有属性和方法
特别注意:我们通过子类能使用的父类成员有public类型的,但是不能直接使用private
【2】使用:非常简单,和没有继承关系差不多的。
通过刚才的例子,大家只是体会到了代码复用。其他的好处,根本没有看到!
经验1:在实际开发中,我们自己使用继承,不要把继承关系搞的太复杂,一般不要超过3层的继承。
经验2:如果你使用的继承过多,或继承的层数多,万一我们如果某些代码写的有问题,你改掉一个后,其他的子类全部
受到影响。(代码地震...)
- 继承与多态的实现
【1】父类的定义
{
//公共属性
//公共方法 //私有方法(只能父类自己使用) //受保护的属性、方法(只能子类内部使用) //父类还有一个重要的作用:(如果你不知道,我们从现实生活中思考:我们可以继承前人为完成的事业,或提出的某些理论)
//言外之意,就是父类可以提出一个未完成的行为(方法),然后让子类去实现!这个就是我们要说的抽象方法!
//虚方法...
}
记住:抽象方法必须放到抽象类中。但是一个抽象类,是可以不包括任何抽象方法的!
【2】子类的定义
子类继承的父类如果有抽象方法,则子类必须重写抽象方法,除非子类也是抽象类。
关于虚方法的使用:
在子类中,子类可以直接完全的使用虚方法的内容,也可以完全不使用虚方法的内容,
当然还可以自己实现,同时继续使用虚方法的内容。
通过调试结论:
抽象方法是不可能被调用的,只有子类重写的方法被调用。父类虚方法是否被调用,取决于子类是否重写,和是否调用。
【3】多态:是面向对象编程语言的程序特性,特点是,当我们调用者使用多态机制的时候,可以不关心具体的实现。
我们就想象成,我们想调用一种行为,但是这种行为可能有多种表现,而具体如何表现,我们程序编写时是无法知道的。 只有程序运行的时候才能确定。这个行为是虚拟机本身特有的。多态就是一种灵活的扩展实现。
多态本质:一种类型,多种行为的实现。
多态告诉我们:面向抽象编程,让你的程序扩展性大大增强!
特别注意:多态的行为,只能限于父类中的抽象方法或虚方法!
多态实现:父类定义抽象方法或者虚方法,不同子类分别重写抽象方法或虚方法即可。
第10次课:接口与多态
多态的出现:为扩展而生!
多态实现形式:通过继承、也可以通过接口
多态实现方法:继承--》父类类型作为方法返回值类型(实际上返回的是一个具体的子类对象)、
父类类型作为方法参数类型(实际传递的是具体的子类对象) 接口--》接口作为方法返回值类型(实际上返回是一个接口实现类对象) 接口类型作为方法参数类型(实际上传递的是一个接口实现类对象)
一、为什么要用接口?
生活:USB接口(上位机的学员会用到很多:串口、网口...)
接口:就是一个标准,一个规范。USB接口:4根线(1根是电源5v、一个是地线、一个是接收、一个是发送)
应用:我们可以按照这个规范,去生成需要的设备接口。
A:当我们开发一个软件的时候,可能很多时候,我们设计完一个对象后,并不需要马上考虑,或者我们也可能不知道
这个对象具体怎么样去编写,我们只是知道的这个对象的行为(也就是知道他干什么,但是不知道怎么干)
还有一种情况,当我们做团队开发的时候,比如一个项目设计了10个模块,由10个小组完成。怎么做到各自做各自的呢?
基于以上的问题,我们可以使用接口解决。
第一、我们设计对象后,可以根据行为直接设计接口(接口就是只有方法规定,没有具体实现)
第二、我们可以把相关的模块都抽象成接口,然后接口直接相互协作,最后不同开发组实现不同的模块,这些模块开发完毕后,项目也就自动集成了。
二、接口怎么使用?
通过实例测试。实例模拟:我们想设计一个人作为父类、然后有校长、教师....
思考:就是说校长和教师的共同行为,能不能都放到父类中?比如,校长和教师的演讲、讲课等这些行为,不见得非得
放到人这个父类中,因为这些并不是人的共同特征。
父类:人类
校长:-->人类
教师:-->人类
教务:人类
后勤:人类
思考:如果我们把校长、教师这些演讲、讲课的行为都放到人类中,那么请问,当你设计教务、后勤人员类的时候,是不是
这些行为自动延续给他们?是的! 这样合理吗?不合理。
怎么办?我们始终要注意,父类一般只放共同的行为,对于部分子类的行为,不能全部放到父类中,因为后面扩展的时候,
这些会自动的延续给子类,所以,我们可以把这部分抽象为接口。
然后呢?谁具有这样的行为,谁实现接口。进行统一的关联。
也就是说接口:侧重的是行为的规范、继承侧重于复用!
- 接口规定:必须以I开头,接口的方法都是没有实现的方法,方法不需要写public。
- 接口可以抽象方法,也可以抽象属性。关键字:interface
3.接口的特点:接口具有强制性,也就是一个类实现一个接口,就必须把接口的所有方法都实现!并且不能改变方法的签名
前面的例子,主要是让大家体会接口在面向抽象编程中的作用,和项目扩展的基本使用。
- 单独添加接口ITeach,然后分别让教务和教师去实现
三、接口和抽象类的区别
1.接口侧重的是功能的封装,抽象类侧重的是代码的复用,虽然两者都可以实现多态,但是我们在扩展中,如果没有要继承的内容时,请直接使用接口。即使有要继承的内容,通常把公共内容放到父类中,然后需要多态的行为,放到接口中。
2.接口更简洁,使用更方便。在框架设计中,抽象的都会变成接口,而不是使用抽象类。
多态的基本原则依据:里氏替换原则。
四、系统接口的使用
接口分类:【1】自定义接口,根据我们实际的项目开发需求,自己写接口。
【2】系统接口,也就是.NET平台已经封装好的,可以给我们直接使用的接口。
常见的系统接口:IList 当我们讲List的时候,有一个对象比较的接口?ICompare
五、接口在设计模式中的应用(重点学习)
六、接口在项目框架中的使用(重点学习)
第11次课:反射Reflection、接口多态与简单工厂设计模式
1、工厂的引入
工厂的概念:生活中工厂是生产产品的,我们在软件中,工厂是创建“对象”的。
工厂的引入:在这里创建Teacher和Dean对象
App.config文件:这个是应用程序配置文件,你可以把程序配置的相关信息,都保存到这个文件中。
- 工厂的使用
【1】定义接口
【2】接口实现
【3】编写工厂
第一、在App.config文件中,配置一个节点,用来保存可变的对象类型
注意:节点名称严格区分大小写,不要写错;必须严格遵守xml的规范。
常见问题:System.TypeInitializationException:““xiketang.com4.ObjectFactory”的类型初始值设定项引发异常。”
主要见到这种问题,就是读取配置文件的问题,怎么解决?
(1)看设置的节点,和读取的节点名称是否一致。
(2)App.config文件中节点 不要写错,如果你写成 是不行的。
(2)如果配置文件内容比较复杂,注意 不要放错位置。
第二、添加引用System.Configuration,同时添加命名空间:System.Configuration
第三、编写对象工厂方法,返回具体接口对象。
- 工厂的好处
第一、对象的使用者,完全不用关心对象是如何创建的,完全解耦。
第二,用户需求变化的时候,通过配置文件轻松实现对象的替换。
问题:如果我们需要再次扩展更多的接口实现类对象,发现,工厂方法内部还得修改!这点还得需要优化。
解决对象在需求变化的时候,创建问题!使用反射!
4.反射技术的应用
关于对象创建:我们可以直接通过new的方式来创建,我们通过new是因为我们明确的知道要创建的对象类型。
但是我们的需求是不同的对象类型!也就是通过new方式直接不可取!
解决方法:必须通过某种方式,找到要创建的对象类型。
问题继续:从哪里找呢?怎么找呢?
从程序集中去找!通过程序集给我们提供的相关类里面的方法去找。
程序集?程序集是应用程序的部署单元。
常见的程序集?控制台程序生成的exe文件就是一个程序集;winform程序生成的exe文件也是程序集;
类库生成的dll文件也是程序集!
程序集里面有什么?我们通过元数据可以查看:
使用命名空间、类名、类的各种成员(属性、方法等)注释信息。
试想:如果我知道一个程序集,能不能找到所有的成员呢?能!
结论:通过控制台的exe文件,我们可以找到这个文件中所有的类和成员信息。
通过反射改进简单工厂,让程序扩展性大大增强。
更多收获:如果我们的程序需要的模块,存在变化,只要你规定好接口,模块是不是可以任意替换?肯定的!
第12次课:OOP练习的目的
学会面向对象的分析、设计。
面向对象分析:就是根据需求,找到我们需要的对象。
面向对象设计:就是把这些对象怎么封装的问题解决。(封装属性和方法)
除此以外就是通过设计,一定要约定好,对象之间的关系(一个对象可以作为另一个对象的属性:1对1,1对多)
大家后面学习框架的话,框架的本质就是让你知道应该设计什么类,这些类有什么要求?怎么组合的!
三层架构:UI -- 》BLL--》DAL--》Models
UI:(界面、边界类,比如打印等...)(属性、字段、方法)
BLL:业务编写、任务传递(隔离作用)(方法居多、属性也有)
DAL:访问数据(数据库、相关文件....)(一般只有方法)
Models:封装数据、传递数据(只有属性)
以上对象之间的关系主要是依赖关系!
ORM框架(根据对象和操作生成一系列与数据库相关的操作和数据。。。)
我们做面向对象的题目,基本思路:
【1】目的:就是找出对象。其次,你要明白不管有多少对象,我们要实现什么?
【2】对象:流程的参与者(动态的(客户、销售员),静态的(手机、...))
【3】流程:实现步骤。
总体三步走:找到类、设计类、添加关系
第13次课:基于反射和接口实现抽象工厂设计模式
授课目标:理解接口的应用、体会设计模式、掌握抽象工厂模式的应用场景
====================================================================
一、重要内容回顾
继承:解决代码复用问题和子类扩展问题。(在.NET平台底层,大量的继承... 控件二次开发、纵向功能扩展)
接口:解决横向扩展问题和团队协作问题。(功能的多态实现,项目框架设计)
继承+接口:相互弥补不足。(接口弥补了继承不能多重继承问题,同时对于接口的灵活扩展问题,继承也是做不到的)
应用:简单工厂设计模式(解决了对象的创建问题?我们把对象的创建交个工厂方法完成!调用者可以不用关心对象是如何创建的,如果我们需求是变化的,也就是可能在使用中从多个对象中选择一个我们需要的,这时候就可以应用此模式)
最大特点:接口==>实现类对象之间(解耦) 接口 接口变量=工厂方法();
二、关于设计模式
基本概念:设计模式是针对软件设计中常见问题的总结。也就是前辈给我们的解决方法。设计模式应用的各种技术的综合
前面我们学过的接口、OOP思想、原则等各种组合。
OOP解决的问题:是整个项目的设计思想(基于对象思考,使用抽象设计)(一切以对象为重)
设计模式解决问题:项目开发中,某些环节的扩展问题。是OOP设计的补充。
三、设计模式分类(根据目的)
1、创建型设计模式
【1】研究问题:对象的创建问题,也就是说将对象的创建和使用分离、从而解耦。(交给第三者、也就是工厂去完成)
【2】主要模式:简单工厂、抽象工厂、单利模式、建造者模式、原型模式等...
简单工厂:一个对象(多个取其一)A B C D
抽象工厂:多个对象(多组取一组)A:A1 A2 A3 B组:B1 B2 B3 C组:C1 C2 C3
单利模式:同一对象(始终就一个)A
2、结构性设计模式
【1】研究的问题:对象的组合问题。也就是对象按照某种方式组合,更好的扩展功能。
【2】主要模式:代理模式、适配器模式、桥接、装饰器模式....
3、行为型设计模式
【1】研究的问题:对象怎么交互和分配职责。也就是对象怎么样相互协作共同完成单个对象无法完成的任务。
【2】主要模式:模板方法、责任链模式、命令命令、中介者模式、观察者模式....
提醒:以上模式,我们常见的几个会了就可以。其他的更多的是我们设计通用框架的时候会用到。
====================================================================
四、抽象工厂设计模式专题讲解
1、应用场景:项目的多数据库支持、业务的多算法封装、以及各种变化的业务。
2、抽象工厂组成:包括抽象产品(业务接口--》可以通过抽象类或接口设计)、实体产品(子类或实现类)
抽象工厂(业务接口和接口实现关联)
3、抽象工厂搭建实现步骤:
任务:搭建一个通用型的抽象工厂业务封装与实现(大家以后都可以按照我讲的这个思路来设计)
步骤:
《1》正常的创建一个解决方案,然后添加你需要的其他的模块。把需要使用抽象工厂模式的任务,预留出来就行。
UI + Models
《2》添加业务接口(抽象产品):具体多少接口,根据你的需要来,这里我们暂定两个。IBizLogicIngerface
IStudentLogic ITeacherLogic 引用:Models
《3》添加业务实现(具体产品):具体产品有多少,取决于业务接口。
引用:Models IBizLogicInterface 第1组:BizInterfaceImpl1 StudentLogicImpl TeacherLogicImpl 第2组:BizInterfaceImpl2 StudentLogicImpl TeacherLogicImpl 注意:我们不管添加多少组,都要注意,每一组的实现类,一定要同名,但是不同的命名空间。 第n组:。。。
《4》抽象工厂方法的编写
解决问题:就是从不同的实体产品中,选择我们需要的一组实体产品。(接口实现类) 如何决定选择的产品呢?把我们的需求配置到配置文件中。 <appSettings> <add key="bizName" value="BizInterfaceImpl1"/> </appSettings>
引用:接口模块
《5》使用业务接口
引用:接口模块
接口实现模块:为了开发调试的方便性,我们也要添加实现类的模块引用,但是项目开发调试完成后,再删除。 (特别注意:如果实现模块不是我们自己开发,不在一个项目中, 只能用复制的方法) 实体模块 抽象工厂模块
调试问题:如果出现对象为null,请注意,可能就是你的命名空间的问题。
总结:当我们配置成第一组实现类的时候,调用的对象全部都是第一组里面的对象。
当然我们配置成第二组实现的时候,调用的就是第二组的各种对象。
优化思考:ProductsFactory这个工厂方法里面的方法,有点太多,写起来很麻烦。
解决方法:使用泛型!泛型后面我们有专题讲解,这里面我们只是给大家做体验!
4、总结
【1】抽象工厂设计模式需要的模块
【2】各个模块之间的引用关系,不要添加乱。
【3】其实本质还是接口多态的应用。
建议的作业:如果你感兴趣,请把接口实现的抽象工厂,改成抽象类实现的工厂。
扩展的学习:录播中,我专门讲解了多数据库的支持。(可以自行学习)
第14次课:C#泛型类和泛型方法
一、泛型(Generic)
1、回顾前面:泛型集合List、Dictionary 所在命名空间:System.Collections.Generic;
比较非泛型:ArryList,Hastable 所在命名空间:System.Collections
经过刚才非泛型集合ArryList里面可以添加任意类型,我们发现,虽然是添加方便的。但是对于数据本身来讲,是非常不安全的。
因为我们开发中很多时候,这些是不可控的。其次,还存在拆装箱的问题。
装箱:将值类型的元素,放到集合中会被转换成object类型。这个过程就叫装箱。
拆箱:将一个集合中的元素取出来,但是这个元素本质是值类型,所以,我们必须强制类型转换。
弊端:如果我们存储大量的数据,我们会发现装箱和拆箱是影响程序性能的。(不测试)
实践应用中:我们发现,很少有情况去在一个集合中,添加任意的数据。
我们的需求:我们不希望在一个容器中,添加不同类型的数据,但是我们又希望这个容器能够根据我们的需求,随时决定能够添加何种类型的数据。
2、泛型出现-->泛型概念:泛型是一种程序特性,当我们使用泛型的时候,确切说定义的时候,是对这个类型不作出明确的规定,
但是当我们使用的时候,必须明确规定,并且不能改变。
3、泛型出现的场合:泛型集合、泛型方法、泛型类、泛型委托...
二、泛型方法
1、为什么要使用泛型方法?
public int Add(int a,int b) { return a+b; } public double Add(double a,double b) { return a+b; } //。。。
基于前面的问题,我们可以定义成泛型方法。
2、泛型方法:就是把一个方法的返回值类型、方法参数定义成泛型类型。
我们常见的泛型类型表示方法:T T1、T2.... 练习1:泛型T,可以用于成员变量的定义类型、方法参数类型、方法返回值类型
3、default关键字
三、泛型约束
目的:就是对泛型类型做出数据类型的要求,或者其他的条件。
四、动态类型
dynamic:当我们希望某一个参数是“特定”类型的时,但是这个参数又被定义成泛型类型,泛型类型是不确定的。
所以在编译阶段,我们是不能这么直接使用的。不过,我们想,我传递数据的时候,肯定会符合你的要求。
所以,我们把数据类型的检查工作延迟到运行时,这时候dynamic就派上用场了。
第15次课:C#委托Delegate和事件Event实战应用
授课目标:掌握方法的“指针”委托技术,为高级编程学习打下基础
一、委托概念
1、概念:Delegate(委托、代理...):委托其实一种程序特性,它特的特点就在于,委托是用来表示方法的。换句话说委托是方法的代表、方法的指针。
int age=10;age其实就是10的代表。
Course myCourse = new Course(); myCourse是一个特定对象的代表。
委托类型 委托变量=具体方法(可以是1个,也可以是多个)。委托就方法的变量。
2、为什么要使用委托?
生活中想象一下,比如我们做一件事情,但是我们自己无法直接去完成。但是我们通过别人可以帮我们完成。
我--->其他人(委托)--->做事情
通过刚才的比较,我们发现,前面age、myCourse等代表的都是某种“静态数据”。
委托代表的是“行为”,就是方法。
软件开发中,我们也会遇到类似情况,那就是我们本身想调用一个行为(方法)但是这个方法因为再不同的对象中,
我们可能直接调用不了,这时候就可以使用委托。
正常情况下:A对象中,创建了B对象,这时候,如果B对象中有一个公共行为C,那么我们在A中是能调用的。
class A
{
B b=new B(); void MyMethod() { b.C(); } void NewMethod(){}
}
问题:如果B想调用A中的行为NewMethod是否可以?当然不行!但是通过委托是可以的。
二、委托的基本使用
- 声明委托(定义方法的原型:方法的返回值类型、方法的参数类型和个数)
2.根据委托编写具体方法
- 创建委托变量(委托是引用类型)
- 将委托变量和一个或多个符合委托定义的具体的方法关联。
- 通过委托变量使用具体的方法(不是直接使用方法)
结论:通过委托变量,可以轻松的调用它所关联的具体方法。
扩展:如果我们给委托变量,同时关联了多个方法,当我们使用委托变量的时候,方法会按照你关联的顺序依次调用。
以上就是我们常说的“多路委托或叫做多播委托”。
好处:我们不仅能动态的增加委托对方法的关联,还可以动态的移除方法的关联。
三、委托的实战应用(一):在多窗体通信中的使用
1、在主窗体A中,创建了若干子窗体B的对象。
现在B窗体需要调用A中的一个方法。正常情况是不行的。但是可以通过委托实现。
2、提示:我们使用委托完成任务的时候,委托变量在哪里定义的技巧是:在哪里使用,就在哪里定义!(谁使用谁创建)
委托变量和具体方法的关联,通常是分开的。一般就是在具体方法定义的地方,进行关联。
总结: B1->A B2->A B3->A
扩展:通过委托如何实现消息的广播?
在哪里接收消息,就把接收消息的方法写到哪里!这里是B窗体。
四、委托的实战应用(二):解决在容器嵌入窗体时的切换问题
五、事件Event(和委托比较)
随便找一个: this.btnCreateChildForm.Click += new System.EventHandler(this.btnCreateChildForm_Click);
public event EventHandler Click
public delegate void EventHandler(object sender, EventArgs e);
通过观察按钮的事件,发现了事件其实是委托的进一步包装。
定义事件:(直接看代码)案例中,我们实现和前面一样的功能,但是这次通过事件来实现。
事件概念:事件其实是对象对外界信息的刺激,产生的一种消息响应机制。
本质:事件其实是委托的进一步包装。
事件的参与者:
【1】发送者(sender):就是对象本身,当时本身信息状态变化的时候,触发一个事件,并通知所有的接受者接收。
passMsgEvent(this.txtSendMsg.Text, this.Text);
【2】接受者(Receiver):就是事件的处理者,在事件发送者触发后,会自动执行的这段代码。
private void ReceiveMsg(string msg, string childName) { this.txtContent.Text += $"来自:{childName} 的消息:{msg}\r\n"; }
事件和委托对比不同点:
第一、事件无法直接赋值,比如事件=null;会出现编译错误,而委托可以。
好处:避免用户对事件直接操作,比如Click事件,如果允许Click=null,会把底层代码清除!可以起到保护。 委托相对太“开放”。
第二、event对象没有invoke()方法,只能通过使用括号的方式来运行。
委托和事件的选择:
第一、正常解决问题,你使用委托和事件没有什么本质区别。所以,我们建议是使用委托。
第二、如果我们做控件二次开发,扩展控件的事件的时候,那必须用事件。
第16次课:《C#匿名方法、Lambda表达式和各种泛型委托》
授课目标:为后面Linq查询和高级C#编程机器理论研究做铺垫
====================================================================
引出:通过前面的讲解,我们知道委托非常强大,首先我们要使用委托解决的就是对象之间信息的“逆向传递问题”。
其次,其实委托还有很多的用途。(匿名方法、Lambda表达式,和泛型结合)最后,我们在学到一定程度的时候,
我们必须研究底层原理。(比如我们学EntityFramework,linq各种查询,扩展方法)
一、匿名方法、Lambda表达式
1、匿名方法的概念:一个方法没有具体的名称,而只有关键字delegate、方法参数、方法体,这种方法是匿名方法。
匿名方法的好处:将具体方法和委托直接关联到一起,如果我们基于委托只需要一个方法的时候,匿名方法肯定是显得简单。
- Lambda表达式:在C#3.0出现的。使用这种表达的可以更简练的编写代码块。CalculatorDelegate cal3 = (int a, int b) => { return a + b; }; CalculatorDelegate cal4 = (a, b) => a - b;
【1】在Lambda表达式中参数类型可以是明确类型,也可以是推断类型。
【2】如果是推断类型,则参数类型可以由编译根据上下文自动推断出来。
【3】运算符=> 读作goes to ,运算符左边输入参数(如果有),右边是表达式或语句块。
【4】表达式两种方式:
(input args)=>表达式 (input args)=>{ 语句1;语句2;语句3....}
【5】Lambda表达式和匿名方法的比较
第一、Lambda表达式本身就是匿名方法。 第二、Lambda表达式允许不指明参数类型,但是匿名方法必须要明确。 第三、Lambda表达式允许单一的表达式或多条语句组成,而匿名方法不允许单一表达式。
二、自定义泛型委托
1.为什要使用泛型委托?普通委托在数据类型的限定上非常严格的。有时候我们需求变化,可能适应不了。
2.泛型委托定义:本质上和泛型方法是非常相似的,泛型委托关联的时候,可以是具体方法、匿名方法、lambda表达式
3.问题引出:仔细研究我们写的泛型委托: public delegate T MyGenericDelegate(T param1, T param2);
试想:如果我们使用4个参数?5个参数?8个参数?按照目前思路,不得不定义很多这种泛型委托!很麻烦!
三、系统泛型委托
针对问题的解决:.NET平台早就为你考虑了这种简单的泛型委托,那就是人家已经定义好了,我们可以直接使用!
两种方式:一种是有返回值Func,一种是没有返回值的Action。
Action<>在多线程中用的非常多。
Func<>在扩展方法中用的非常多。
请直接参考实例代码。
结论:泛型委托作为方法参数,实际上传递的是一个具体方法或一个Lambda表达式! (多态一种表现)
第17次课:.NET控件二次开发技术和用户控件开发
回顾:继承、接口、泛型、委托、事件
为什么要学习控件二次开发技术?普通的VS自带的这些控件,可能并不能完全满足我们的需求,这时候我们就可以对现有控件
做封装,也就是扩展功能,这就是二次开发。针对是一个控件的功能扩展。
目的:是希望我们学会二次开发的方法,而不是单独的技术点。
用户控件:是将现有的多个控件组合到一起,构成一个新的控件。当我们需要的功能以模块化方式出现的时候。
一、实现文本控件的验证功能
1、建议:大家学会今天的方法,后面可以自己在业余时间多封装一下相关的其他的控件。
2、用户控件也好,二次开发的控件也好,必须放到类库中。
对现有控件做二次封装,我们都用组件类。 结论:通过观察控件的继承关系我们发现,所有的控件都继承自Control,Control : Component
关于正则表达式,就是用一组特殊的字符串,表示一个规则,这个规则,可以去检查我们需要的字符串,是否符合这个规则
因为这个规则,按照一定的方式组合,所以,可以验证任意的你需要的字符串。
正则表达式,具有很强的跨平台特性,也就是不区分语言,大量的前端后台都可以用。
我们的要求:大家只要能使用现有的以及编好的验证规则就行。
第一个数字,第二个是A-D的字母,第三个0-9的数字
0C9
A00
3、封装一个通用的正则表达式验证方法
好处:功能非常强大,只要你自己会写正则表达式就可以。
不足:就是每次验证,你必须都得记住或找到这些正则表达式,但是对于这些常用的就不太方便。
解决:把常用的正则表达式验证再做一次封装。
二、扩展下拉框功能
直接看讲解....
验证功能,我们给大家就说这些,其他的,可以自己开发(建议自己思考,如果你有好的想法,做完了,可以给老师看一下)
三、二次开发控件的使用
- 如果你是针对项目的,可以带着源代码。
- 如果你的二次开发控件非常成熟,或者你自己使用,或者给别人使用,直接讲dll控件给他即可。
- 我们把dll拖放到工具项目中即可。
四、用户控件
1、概念:用户控件顾名思义就是我们用户自己的组合控件。不是针对某一个特定的控件做功能扩展。
2、用途:非常的广泛,你可以根据你的项目需求,把相同的部分都组合成用户控件,在什么地方使用,你就随时拿过来。
扩展:如果你学了后面的web开发,也会大量使用。
第18次课:《基于T-SQL快速完成数据库和数据表创建》
授课目标:为后续数据库项目开发做准备
====================================================================
本节内容:在预科中我们已经详细讲解了。
一、学习数据库应该学什么?
- 数据库类型:关系型数据库(SQLServer-->MySql-->Oracle--和其他的小型的关系型数据库) 表-->一条一条的数据(就是标准化) -->非关系型数据库(缓存数据库)
- 关系型数据库,最大的特点:就是全部都用的统一的结构化查询语言(SQL) T-SQL
- 应该学习哪些内容?【1】正确的创建数据库和数据表、各种约束(使用脚本)【2】正确的添加测试数据【3】数据库的各种操作(重点是查询、添加、修改、删除)【4】熟练的编写数据库视图和存储过程(主要用来提升性能的)、索引【5】 数据安全(首先要保证输入的和操作的数据是安全--就是一致性)学习事务、触发器慎重【6】数据库的相关的关联(备份、恢复、定时任务、日志查看....)最后:请研究数据库的设计(在这个之前,你最好把我们讲的,或者项目用的数据库,研究一下)
- 数据库开发环境的熟悉
【1】登录数据库(注意登录的几个条件)
--》服务器名称:如果是本机相对简单,这个服务器名称或者是电脑名称,或者是IP地址。
但是,请注意你的安装实例。(安装实例:就是你安装数据库的服务,这个服务通过计算机的“服务”列表查看) 我的电脑上面的服务情况是:SQLServer(MSSQLSERVER) 这个是默认实例,如果是默认实例我们登录数据库的时候 服务器名称:就是电脑名称或IP地址。或者“.”
--》命名实例:SQLServer(XIKETANG) SQLServer(SQLEXPRESS)
在一台计算机上,如果安装了默认实例,第二次安装不同版本的数据库的时候,就必须用命名实例。 服务器名称:电脑名称或IP地址或者"."\实例名称 比如:AGOD21-06021316\xiketang 记忆:\是给windows操作系统用的。 /这个是给linux操作的时候,我们通常用的。
--》身份验证:windows身份验证(这个一般都是直接登录,权利最大)但是这个只能用于本机。不能局域网或远程。
SQLServer身份验证(账号、密码) sa账号,具有全部的操作权限。 sa账号密码的修改:总结了4个步骤。演示,大家必须会!
【2】会使用查询分析器(新建一个查询,我们以后都用这个)
二、数据库脚本的使用(T-SQL创建数据库和数据表)
- 创建数据库
- 创建数据表
- 创建约束
- 添加测试数据
- 数据库的基础操作
数据库-->若干数据表-->若干字段
补充:常用的数据库的类型(系统数据库(master)、用户数据库)
数据库文件包括:主数据文件(必须有,且只能有一个)次要数据文件(可以有多个)日志文件(至少有一个)
三、标识列的使用
identity(100000,1)
- 使用场合:如果你需要设计一个字段是自动增长的字段,那你就可以直接使用。 如果你的数据表的列,没有能够唯一区分的列,这时候,我们可以使用自动增长列(本质是没有什么特殊意义) 仅仅是为了区别而已。同时,也会再给当前列设置主键约束。 主键:不仅唯一,重要的影响数据的索引。
- identity(标识种子,增长量)
- 注意问题:使用普通的delete删除数据后,标识列不会自动补全的。比如:100005 100006(被删除) 1000007
第19次课:《基于T-SQL快速完成数据库和数据表创建》
授课目标:为后续数据库项目开发做准备
====================================================================
本节内容:在预科中我们已经详细讲解了。
一、学习数据库应该学什么?
- 数据库类型:关系型数据库(SQLServer-->MySql-->Oracle--和其他的小型的关系型数据库) 表-->一条一条的数据(就是标准化) -->非关系型数据库(缓存数据库)
- 关系型数据库,最大的特点:就是全部都用的统一的结构化查询语言(SQL) T-SQL
- 应该学习哪些内容?【1】正确的创建数据库和数据表、各种约束(使用脚本)【2】正确的添加测试数据【3】数据库的各种操作(重点是查询、添加、修改、删除)【4】熟练的编写数据库视图和存储过程(主要用来提升性能的)、索引【5】 数据安全(首先要保证输入的和操作的数据是安全--就是一致性)学习事务、触发器慎重【6】数据库的相关的关联(备份、恢复、定时任务、日志查看....)最后:请研究数据库的设计(在这个之前,你最好把我们讲的,或者项目用的数据库,研究一下)
- 数据库开发环境的熟悉
【1】登录数据库(注意登录的几个条件)
--》服务器名称:如果是本机相对简单,这个服务器名称或者是电脑名称,或者是IP地址。
但是,请注意你的安装实例。(安装实例:就是你安装数据库的服务,这个服务通过计算机的“服务”列表查看)
我的电脑上面的服务情况是:SQLServer(MSSQLSERVER) 这个是默认实例,如果是默认实例我们登录数据库的时候
服务器名称:就是电脑名称或IP地址。或者“.”
--》命名实例:SQLServer(XIKETANG) SQLServer(SQLEXPRESS)
在一台计算机上,如果安装了默认实例,第二次安装不同版本的数据库的时候,就必须用命名实例。 服务器名称:电脑名称或IP地址或者"."\实例名称 比如:AGOD21-06021316\xiketang 记忆:\是给windows操作系统用的。 /这个是给linux操作的时候,我们通常用的。
--》身份验证:windows身份验证(这个一般都是直接登录,权利最大)但是这个只能用于本机。不能局域网或远程。
SQLServer身份验证(账号、密码) sa账号,具有全部的操作权限。 sa账号密码的修改:总结了4个步骤。演示,大家必须会!
【2】会使用查询分析器(新建一个查询,我们以后都用这个)
二、数据库脚本的使用(T-SQL创建数据库和数据表)
- 创建数据库
- 创建数据表
- 创建约束
- 添加测试数据
- 数据库的基础操作
数据库-->若干数据表-->若干字段
补充:常用的数据库的类型(系统数据库(master)、用户数据库)
数据库文件包括:主数据文件(必须有,且只能有一个)次要数据文件(可以有多个)日志文件(至少有一个)
关于创建数据表的建议:我们根据数据库的设计,首先创建没有外键关系的表。就是因为后面我们添加外键表,需要创建外键关系的时候
要引用主键表。否则就找不到。
三、标识列的使用
identity(100000,1)
- 使用场合:如果你需要设计一个字段是自动增长的字段,那你就可以直接使用。 如果你的数据表的列,没有能够唯一区分的列,这时候,我们可以使用自动增长列(本质是没有什么特殊意义) 仅仅是为了区别而已。同时,也会再给当前列设置主键约束。 主键:不仅唯一,重要的影响数据的索引。
- identity(标识种子,增长量)
- 注意问题:使用普通的delete删除数据后,标识列不会自动补全的。比如:100005 100006(被删除) 1000007
四、关于约束问题和数据插入问题
1、为什么要使用约束?也就是约束是保证数据有效性,和正确性的最后一个屏障。(应用程序验证也是保证数据合法性)
2、约束类型:主键约束-->要求唯一、并且不能为null,数据存储默认会参考主键约束。用来保证数据唯一的最好方法。
标识列--》自定维护的,也是唯一的。标识列一般都会和主键挂钩,当我们不能够选出一个字段作为主键的时候,可以使用。 注意不能为标识列显示的插入值。
常见错误:当 IDENTITY_INSERT 设置为 OFF 时,不能为表 'Students' 中的标识列插入显式值。
以上两者都是为了保证实体(数据)唯一能够区分的。保证某一行的。 检查约束Check:保证某一列的字段值,在我们希望的范围内。 默认约束default:就是给某一个字段提供一个默认的数据。 外键约束foreign key :主要是表直接的关联约束。比如我们在外键表中插入数据的时候,外键的值(比如Students表中的 ClassId)会自动的和关联的主键表(StudentClass)中的对应主键ClassId做检查,也就是说你在外键表中插入的外键数据 必须在主键表中存在。否则就会出错。
常见错误:INSERT 语句与 FOREIGN KEY 约束"FK__Students__ClassI__173876EA"冲突。该冲突发生于数据库"StudentManageDB",表"dbo.StudentClass", column 'ClassId'。
语句已终止。
insert into Students ( StudentName, Gender, DateOfBirth, Age, StudentIdNo,PhoneNumber, StudentAddress, ClassId)
values('王小虎','男','1989-08-07',22,120223198908071111,'022-22222222','天津市南开区红磡公寓5-5-102',5)
原因:因为外键表中ClassId只有1 2 3 4 这四个,而我们现在用的是5,5不存在与主键表中所以出现这个错误。
其他错误:INSERT 语句中列的数目大于 VALUES 子句中指定的值的数目。VALUES 子句中值的数目必须与 INSERT 语句中指定的列的数目匹配。
这种错误就是我们SQL语句中,字段个数和Values中的字段值个数不同导致的。
五、数据的基本操作(更多详见录播)
- 表示所有的通配符。我们在测试中使用,程序中一般不用
关于附近有语法错误的解决:select * from ScoreList SQLServerDB is null 关键字 'is' 附近有语法错误。
附近:就是这个关键字的前面或者后面,多数情况下是紧挨着关键字的前后,有时候也可能间隔一个
六、视图(View)
- 为什么要学习视图?
在没有视图之前,我们都是写各种各样的SQL语句,有点,非常灵活。后面我们学习应用程序开发的时候,通过C#发送过来的SQL语句
到达数据库的时候,会执行什么过程呢?
数据库接收到各种应用程序发送的SQL语句的时候,通常的流程是这样的:
【1】语法检查-->【2】优化(根据你的查询条件内容,和索引情况等,综合优化一下)-->【3】编译-->【4】执行
我们想,常见的查询,尤其是组合查询,能不能把查询固化到数据库服务器端,然后客户端使用的时候,只是调用一下呢?
当然可以,这个就是我们要学习的存储过程和视图。
视图其实就是一个查询,本身没有数据,就是把我们要查询的SQL语句,经过优化后经过编译存储到数据库服务器端。
视图我们本身也可以把它看成一个“没有数据的表”。
2.视图的创建与使用(见代码)
视图和普通的SQL语句查询比较
【1】视图本身就是一个提前写好的查询。但是这个查询保存到数据库服务器端。应用程序通过调用可以直接执行。
【2】普通的SQL语句查询。它在应用程序端。要执行,需要通过程序把SQL语句发送到数据库服务器端。
【3】后期的查询维护不同。如果我们要通过应用程序发送SQL语句,你必须要修改程序。但是如果我们使用视图呢?
可以适当的直接在数据库服务器上修改。
- 视图本身的特点:本身不保存任何数据,只是保存到服务器端的一段编译好的SQL语句而已,并且已经优化好和编译好。
- 关于使用建议:我们在学习阶段,必须会使用脚本写视图。如果是正式开发阶段,我们可以适当的使用可视化方式创建视图。创建完毕后,把SQL语句优化好,再复制到你要创建的视图中。这个仅仅是为了省时间。必要的时候可以使用。
- 视图还有一个非常重要的功能:比如我们在项目中会根据不同的角色,做不同的数据查询,这时候,我们在必要的时候完全可以
针对不同的用户角色,设计不同的视图,保证数据的安全。
第20次课:数据库存储过程各种情况详解和事务安全
七、存储过程
- 概念:存储过程就是编写到数据库服务器端的SQL语句,存储过程的内容不仅仅是可以实现查询,也可以实现CRUD同时操作。
使用选择:如果仅仅是查询,建议你使用视图。尤其是针对不同的角色需要调用不同的视图。这时候视图是非常方便的。
- 分类:系统存储过程(请大家自己学习一下,知道即可,可以随时查询)-->系统给我们提前定义好的,我们可以直接使用。用户自定义存储过程(我们主要用的)
- 好处:
【1】执行效率高。已经编译好,并保存到服务器端。只需要调用,必要的时候传递参数即可。
减轻客户端的压力。
【2】安全型好。客户端调用,只需要发送指令即可。数据安全有保障。
【3】维护方便。如果后续有改动,可以直接在服务器端修改即可,而客户端程序不用修改。
- 调用:
【1】在数据库服务器端,写脚本调用。(必须要会)
【2】在应用程序中调用。
- 编写(具体见代码)
建议:学习SQLServer自己必须要有一本参考书。
八、事务
- 为什么要使用事务?
通过刚才示例的观察,我们发现,当一个存储过程或多个SQL语句(指代insert、update、delete类型)依次执行时候,
如果其中一条或几条发生错误,但是其他的还会继续执行,会造成数据的不一致,非常危险。常见的,比如银行,我们从一个账号
转钱到另一个账号,如果当我们转出的时候,系统发生了故障,但是没有转入,这样的话,银行的数据就出问题。
想解决这个问题,我们使用事务!
- 事务的特点
原子性:就是不可分割性,是一个整体。
一致性:也就是我们操作的数据,前后都保持高度的一致。
- 主要作用:就是保证数据在不同的操作中,或者这些操作全部成功,或者失败的时候全部取消。
- 事务的创建和使用(示例代码)
在SQLServer中全局变量使用@@标识
常见的:@@Identity @@error 用来存储SQL操作最后一条语句的状态。
通过练习,大家充分感觉到使用事务的重要性!
建议:如果你在开发中,使用存储过程,只要是两个或两个以上的insert、update或delete类型的SQL被执行,你就都可以
使用事务。
后续扩展:我们在C#应用程序中,如果我们发送的SQL语句,有多个insert或update、delete,同样在C#中也可以直接使用
事务。
第21次课:基于C#开发高效的ADO.NET数据访问对象
必备的基础:预科中,给大家详细的讲解了ADO.NET常用的操作数据库的各种方法!
内容的引出:当我们学会了C#编程,我们又懂得了数据库的数据操作,我们开发软件的目的其实多数情况是为了处理数据。
所以,我们用的C#开发语言,第一个要解决的问题,就是如何和数据库交互的问题。因为数据库客户端是给 我们开发者或者专业的数据库人员准备的,用户是不会用的。用户用的是C#开发的应用程序。
一、相关的概念和数据访问基础
1、ADO.NET概念:也就是在.NET平台上的ADO数据访问技术。都是通过.NET平台提供的各种数据访问对象的组合实现的。
2、ADO.NET包括:对数据库的各种CRUD。后续会学习ORM框架(主要解决是不在直接使用ADO.NET各种对象)。
3、ADO.NET对象:
【1】链接数据库:Connection对象
【2】操作数据库:Command对象(CRUD)
【3】数据读取对象:DataReader对象
【4】内存数据库:DataSet
在使用这些对象之前,你要知道.NET平台为了更好的兼容各个数据库,使用了统一的接口,然后不同的数据类型操作的时候,
使用的对象名称是不一样的,但是对象的操作方式完全一样。
比如:我们操作SQLServer数据库,我们使用SqlConnection、SqlCommand、SqlDataReader
4、如何连接数据库?
【1】四个条件:服务器名称(或IP地址)、访问的数据库名称、服务器登录账号、密码
【2】条件封装:将以上四个条件按照格式封装到字符串中。
//如果是命名实例,必须按照如下方式写
"Server=AGOD21-06021316\SQLExpress;DataBase=SaleManagerDB;User ID=sa;Password=a123456"
//如果不是命名实例,服务器名称可以简化
"Server=.;DataBase=SaleManagerDB;Uid=sa;Pwd=a123456"
注意:关键字不要写错。不区分大小写,但是注意英文半角。
5、操作数据库(实践1)
问题:经过我们刚才链接数据库的测试,我们已经意识到,后面我们开发中,在不同的模块中,会大量的使用数据库的链接和操作。
那我了实现代码复用,我们通常要设计一个专门的数据访问对象SQLHelper
System.TypeInitializationException:““System.Data.SqlClient.SqlConnection”的类型初始值设定项引发异常
出现这个错误的解决方法:
【1】首先检查配置文件App.config文件或web.config文件是不是放到项目的根目录。
【2】配置文件必须是App.config,或web.config 不能自己命名。
【3】检查配置文件节点的位置是不是放错。应该放到这个节点下面。
【4】请检查节点名称是不是 ,注意,很多学员可能丢掉后面的s
【5】请检查读取的节点中属性的名称是否写错,比如: name="connStrings"
那么读取的时候,就应该是:ConfigurationManager.ConnectionStrings["connStrings"].ToString();
有时候,你在后面多一个空格也不行:name="connString "
6、操作数据库(实践2)
System.Exception:“调用ExecuteNonQuery方法发生异常,具体异常信息:“2”附近有语法错误。”
只要是出现。。。。附近有语法错误,一定要找这个关键字的前后,比如我们看2的前后,发现SQL语句有问题。
一定要观察生成的SQL语句,不是你编写的那个SQL语句。
insert into Products (ProductId,ProductName,UnitPrice,Unit,Discount,categoryId)values('{0}', '{1}', {2}, '{3}',{4}, {5})insert into Products (ProductId,ProductName,UnitPrice,Unit,Discount,categoryId)values('1000900001', '可乐', 10.5, '瓶',0, 3)
我们发现SQL语句重复了。这时候,我们就要看在哪里拼接的SQL语句,就去哪里看!我们发现:
string sql = "insert into Products (ProductId,ProductName,UnitPrice,Unit,Discount,categoryId)";
sql += "values('{0}', '{1}', {2}, '{3}',{4}, {5})";
sql += string.Format(sql, "1000900001", "可乐", 10.5, "瓶", 0, 3);
最后一个应该是sql= string.Format(sql, "1000900001", "可乐", 10.5, "瓶", 0, 3);
问题:在实际开发中,使用占位符的方式,会有潜在的危险。(注入式攻击)
解决方法:通过带参数的SQL语句和存储过程实现(推荐)
注意:在SQL语句中,参数的名称和个数,必须要和参数数组中的参数名称和个数完全一致。
错误:
参数化查询 '(@ProductId nvarchar(10),@ProductName nvarchar(2),@UnitPrice int' 需要参数 '@Discount',但未提供该参数。
经过对比我们发现参数封装: new SqlParameter("@Discount",0),能确定名称是一样的。
个人经验分享:我们在C#中,写SQL语句的时候要尽量少,比如在数据库中需要使用默认值的,都在数据库端实现。
尽量不要在程序中实现。
第23次课:【基于实体类的数据封装和项目分层架构设计】
一、基于我们现有的基础,假如开始项目开发,该如何做?(在我们预科中,专门讲过项目分层)
演示没有分层情况下,程序是如何编写的。
我们发现的问题:
【1】在UI中,按照对象职责明确原则,UI就是用来获取用户数据,或给用户展示数据的,也就是说UI不应该
直接操作数据库。因为如果UI职责过多,当功能复杂时,后续维护非常麻烦。
【2】代码显得很不优雅。项目各个功能全部耦合到一起。也不符合我们OOP中,高内聚低耦合的做法。
解决方法:使用任务分层设计(每一个层,可以作为一个模块,完成自己的任务,各个层之间建立一个关联)
二、项目分层的基本方法
1、项目需要的核心层?
【1】UI表示层:获取用户信息、向用户展示数据
【2】DAL数据访问层:主要用来操作数据(可以是数据库(关系型数据库、非关系型)、也可以是其他的数据源)
就是各种CRUD方法。就是具体的一个数据操作。 主要任务:执行数据操作。不参与业务处理。 常用的类:通用数据访问类、一般数据访问类
【3】BLL业务逻辑层:主要是介于UI和DAL之间,在业务复杂情况下处理具体的业务问题。相当于公司的中层管理。
主要任务:第一、传递数据(不需要处理的情况下) 第二、业务处理(算法封装、业务计算) 常用的类:和一般数据访问类对应的业务逻辑类、特别封装的相关业务类(主要靠经验) 特别注意:在业务逻辑中,是不能操作控件的。
【4】接口层:专门封装业务接口,或者数据接口。
【5】服务层:API接口,主要是对外提供服务的,分布式开发中经常用。(可以针对不同的平台开发通用 API)
【6】通信层:封装各种通信协议....
【7】实体层:非常重要的,也是必须要有的。主要用来封装数据的。将数据封装成对象。
【8】通用层:放置常用的工具类等。
注意:前面3个必须要有,其他的根据需要添加。
公司的组织结构: 领导层——--》中层管理-------->基层员工
建议:每个层,都独立一个dll类库文件。
2、项目分层设计实践
【1】添加BLL、DAL、UI三部分
【2】DAL中添加:通用数据访问类、一般数据访问类
public int AddProduct(string productId,string productName,double unitPrice,string unit,int discount,int categoryId)
{
//定义SQL语句
string sql = "insert into Products (ProductId,ProductName,UnitPrice,Unit,Discount,categoryId)";
sql += "values(@ProductId,@ProductName,@UnitPrice,@Unit,@Discount,@categoryId)";
//封装参数 SqlParameter[] param = new SqlParameter[] { new SqlParameter("@ProductId", productId), new SqlParameter("@ProductName", productName), new SqlParameter("@UnitPrice", unitPrice), new SqlParameter("@Unit", unit), new SqlParameter("@Discount",discount), new SqlParameter("@categoryId", categoryId), }; //调用通用数据访问类 return SQLHelper.ExecuteNonQuery(sql, param); }
通过前面的写法,我们发现参数非常麻烦,尤其是多的时候,很容易出错。
这时候我们想到了,基于对象编程,一切皆对象,我们应该把参数以对象形式传递过来。
这时候就出现了实体类!
【实体类】
【1】特点:只有属性的类,并且属性名称和类型和对应的数据表是一致的。也就是数据表的映射。
数据表名称--> 实体类名称 (多有多少张数据表,一般就有多少个实体类) 数据表字段名--> 实体类属性名 数据表字段类型-->实体类属性类型 (int--》int;varchar、char等所有文本类型-->string)
【2】用途:第一、封装数据(就是把用户输入的数据,或DAL中查询的数据,封装到实体对应的属性中)
第二、传递数据(从UI中把数据传给下一层、或者从下一层数据封装好以后,传递到上一层)
【3】扩展:我们后面可以根据需要,适当的增加实体类的属性,比如多表链接查询就会用到(放到项目中)
项目之间的引用:
DAL-->DataModels
BLL-->DAL + DataModels
UI-->BLL + DataModels
强化对象之间数据传递过程:
- 通过UI封装对象,也就是把用户输入的数据,封装到对象相应的属性中。这样的话,对象作为一个整体非常容易传递。
- 从UI中将实体对象传递到业务层,业务成层继续传到数据层。
- 数据层将实体中的属性所包含的数据解析出来,封装到SQL的参数中。
- 调用通用的数据访问类,执行SQL语句。执行完毕后,再返回数据,过程正好是相反的。4-->3-->2-->1
实现对象的修改,和添加的过程是完全一样的,大家可以参考预科,自己独立完成。包括删除对象。
实现修改和删除的时候要特别注意:必须要有主键参数。
提醒:主键值一般是不能修改的。不允许用户修改。
建议性练习:请大家自己实现修改、删除。
查询过程封装总结:
- 在对应的数据访问类中,编写查询方法,将查询结果通过循环封装到对象集合中。
- 对象集合到达业务逻辑,处理后,传递到UI
- UI使用集合展示数据。
DataGridView显示数据,绑定需要三个内容:
- dgv列标题:显示文本,一般中文
2.dgv--属性绑定:DataPropertyName
- dgv列名称:Name
实体属性的扩展:
原因:当我们查询时候,经常会遇到不同的数据表联合查询,但是我们的实体类是针对一个表完成的,我可以使用扩展实体。
方法:在现有的实体中,增加需要的相关属性即可。
建议:如果你的查询显示内容,经常变换,在winform程序中,可以适当使用DataTable或DataSet
学习作业:在录播中,我专门讲了“组合扩展实体”。
总结:一般项目我们使用三层结构完全解决。
1、首先设计好数据库
2、根据数据库设计各种实体类。
3、根据实体类添加对应的数据访问类(一般有多少个实体类,就会有多少个数据访问类)
4、根据数据访问类添加业务逻辑类。(实际开发中,一个业务逻辑类,可能对应多个数据访问类)
5、特别注意模块之间的引用关系。
第24次课:《C#多线程编程与线程底层分析》
一、多线程的学习
Thread基础使用-->原理分析-->ThreadPool-->Task(主流)
二、为什么学多线程?
在单核时代,我们写的程序,如果没有多线程,就会经常卡顿,但是并不是说用了多线程程序就绝对不卡顿。因为单核时代,即使你用
多线程,实际上,根本不是并行执行。其实是通过时间片切换,达到“并行”目的。
在多核时代,首先从硬件上,就已经实现了真正的并行执行,但是是不是所有的都能并行执行呢?
三、将同步和异步多线程比较
异步多线程:在一定程度上,能够提升系统的性能。改进用户体验。
四、跨线程访问控件
五、多线程底层观察
windb软件,请大家自行安装。
目的:底层查看工具。通过windbg可以观察CLR级别的信息
启动:首先要加载.NET调试扩展包SOS
SOS所有命令都是以!开头,通过!help查询所有命令。
.loadby sos clr
!theads
六、线程在空间上的开销
- Thead内核数据结构:线程的ID和线程环境都在CPU寄存器中占用空间....
最典型的,比如CPU在时间片切换的时候,那么没有处理完数据,放到哪里?当然是CPU的相关存储空间中。时间片大概30ms左右。
2.用户堆栈模式:用户程序中“局部变量”和“参数传递”所使用的堆栈。
常见的错误:StackOverFlowException《堆栈溢出》,如果你写一死循环,一定会出现这种情况。
因为操作系统默认会分配1M的空间给用户堆栈。用于局部变量和参数传递。
...
七、线程在时间上的开销
- 资源使用通知的开销:因为一个程序会调用很多的托管的和非托管的dll或exe资源.... 线程销毁呢?同样也得通知。
- 时间片切换开销:
通过以上分析,我们使用多线程的时候,必须综合权衡。
八、线程池
当我们开发中,用的线程比较多的时候,就要考虑性能问题。所以,我们可以开几个线程,方便使用。
第25次课:Task和各种任务阻塞、延续及其线程锁Lock
Thread线程和ThreadPool线程池
Thread:我们可以开启一个线程。但是请大家记住:线程开启会在空间和时间上有不小的开销。所以,不能随便开。
ThreadPool:会根据你的CPU的核心数开启一个最合适的线程数量。如果你操作中,非常耗时,就不要用线程池。
A B C D ....
C#4.0时代,出现了Task。
Task=>Thread + ThreadPool结合 (通过windbg自己查看)
VIP课程的学习资源:
- 预科(预科是让你快速从0基础到项目的一个学习过程)目的:基础语法、基础面向对象、基础数据库操作、基础的项目开发模块基本掌握。特点:后面有很多细节,并没有讲,但是预科中也没有用到。要求:不要太过于纠结知识点。时间:建议1个月完成。1.5月。
- 直播(直播是系统讲解的,学完预科,可以直接来看直播)项目1:第12次之后,你就可以做“学员管理系统的项目”(非常经典的项目)项目2:第23次之后,你就可以做“超市前台结算系统和后台管理系统、图书管理系统”时间:2个月后面web部分:大概1-2个月
- 录播(也可以,只不过前面有一部分和预科是直接重复)webform可以不学,学完前面2节课,可以直接学习MVC。时间:1.5半月。
强调:必须要动手,把我课程讲的都做一遍,项目前面的至少做3遍。
教材:作为辅助参考。
第26次课 补充课程 :Socket通信
1、先是IP地址后是端口号
2、服务端和客户端:需要负责监听的socket
3 、TCP协议(三次握手完全成功后才能进行客户端和服务端的连接)
4、UDP协议(直接发,不管有无接收,数据容易丢失)
5、
第27次课 ModbusRtu通信测试平台实现
①如何复制已完成的上位机到自己的界面中
拷贝以下三个文件
这三个文件复制到这个位置
添加现有项,找到刚刚那个目录。
选中.cs文件即可
1、搭建modbusTCP仿真环境
①快速搭建一个ModbusTCp服务器?ModbusSlave
②可以用西门子plc来做
③搭建一个西门子plc仿真环境
start运行 正常是黄色状态
2、ModbusTCP通信协议报文格式
3、写一个简单的通信