C#笔记
剖析Hello,world程序
这篇笔记是我去年看刘铁猛和别的老师的课程写的笔记,可能有点潦草,但是还是放在这里吧
撑场子(不是
- 类(class)构成程序的主体
- 名称空间(namespase)以树形结构组织类(和其他类型)
- 例如Button和Path类
using System;//使用了System的名称空间
namespase Helloword
{
class Program
{
static void Main(String[] args)
{
Console.WriteLine("Hello,World!");
//System.Console.WriteLine("Good Morning.");
}
}
}
using System.IO;
namespace Conflict
{
public partial calss MainWindow:Window
{
public MainWindow[]
{
InitializeComponent();
System.Io.Path();
}
}
}
类库的引用
- 类库引用格式使用名称空间的物理基础
- 不同技术类型的项目会默认引用不同的类库
- DLL引用(黑盒引用,无源代码)
- NuGet简介
- 项目引用(白盒引用,有源代码)
不同的项目模板就是引用不同的类库
DLL引用
MyLibrary.dll包含名为Tools的名称空间。
Tools名称空间中包含有Calculator类。
Calculator类的方法包括:
Add - 用于计算加法
Sub - 用于计算减法
...
References->Add References->browse->MyLibrary.dll
using System;
namespase Helloword
{
class Program
{
static void Main(String[] args)
{
double result = Tools.Calculator.Add(1.2, 3.4);
Console.WriteLine(result);
}
}
}
4.6
Form的引用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ConsoleHelloWord
{
class Program
{
static void Main(string[] args)
{
Form form = new Form();
form.ShowDialog();
}
}
}
只有DLL,没有源代码,几乎可以说是“蒙着眼睛引用类库”,这是很危险的,特别是对于大型的项目。
NuGet引用
引用->管理NuGet程序包
项目引用
右键解决方案->添加->现有项
引用 ->solution
ConsoleHelloWorld->Program.cs
using System;
namespace ConsoleHelloWord
{
class Program
{
static void Main(string[] args)
{
//Entity Framework
Console.WriteLine(ClassLibrary2.Class1.Add(1, 2));
}
}
}
ClassLibrary2->Class1.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClassLibrary2
{
public class Class1
{
public static double Add(double a, double b)
{
return a + b;
}
}
}
依赖关系
- 类(或对象)之间的耦合关系
- 优秀的程序追求“高内聚,低耦合”
- 教学程序往往会违反这个原则
- UML(通用建模语言)类图
排除错误
- 仔细阅读编译器的报错
- MSDN文档与搜索引擎结合
类,对象,类成员简介
类与对象的关系
- 对象也叫实例,是类经过“实例化”后得到的内存中的实体
- Formally "instance" is synonymous with "object" ——对象和实例是一回事
- “飞机”与“一架飞机”有何区别?天上有(一架)飞机——必须是实例飞,概念是不能飞的。
- 有些类是不能实例化的,比如“数学”(Math class),我们不能说“一个数学”
- 依照类,我们可以创建对象,这就是“实例化”
- 现实世界中常称“对象”,程序世界中常称“实例”
- 二者并无太大区别,常常混用,初学者不必迷惑
- 使用new操作符创建类的实例
- 引用变量与实例的关系
- 孩子与气球
- 气球不一定有孩子牵着
- 多个孩子可以使用各自的绳子牵着同一个气球,也可以都通过一根绳子牵着气球
using System.Windows.Forms;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
(new Form()).Text = "My Form";
(new Form()).ShowDialog();
}
}
}
using System.Windows.Forms;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
Form myForm;
myForm = new Form();
myForm.Text = "My Form";
myForm.ShowDialog();
}
}
}
类的三大成员
- 属性(Property)
- 存储数据,组合起来表示类或对象当前的状态
- 方法(Method)
- 由C语言中的函数(function)进化而来,表示类或对象“能做什么”
- 工作中90%的时间是在与方法打交道,因为它是“真正做事”、“构成逻辑”的成员
- 事件(Event)
- 类或对象通知其他类或对象的机制,为C#所特有(Java通过其他方法实现这个机制)
- 善用事件机制非常重要
- 使用MSDN文档
- 某些特殊类或对象在成员方面侧重点不同
- 模型类或对象重在属性,如Entity Framework
- 工具类或对象重在方法,如Math,Console
- 通知类或对象重在事件,如各种Timer
静态成员与实例成员
- 静态(Static)成员在语义上表示它是“类的成员”
- 实例(非静态)成员在语义上表示它是“对象的成员”
- 绑定(Binding)指的是编译器如何把一个成员与类或对象关联起来
- 不可小觑的“.”操作符——成员访问
C#语言基本元素概览,初识类型、变量与方法,算法简介
构成C#语言的基本元素
- 标记(Token)(对编译器有意义的语句)
- 关键字(Keyword)
- 基本关键字:
- 上下文关键字:
- 基本关键字:
- 操作符(Operator)(也叫运算符)
- 标识符(Identifier)(给类、变量、方法等取的名字)
- 什么是合法的标识符
- 怎样阅读语言定义文档
- 大小写规范
- 命名规范
- 什么是合法的标识符
- 标点符号
- 文本(字面值)
- 整数
- 多种后缀
- 实数
- 多种后缀
- 字符
- 字符串
- 布尔
- 空(null)
- 整数
- 关键字(Keyword)
- 注释与空白
- 单行
- 多行(块注释)
初识类型、变量和方法
- 初识类型(Type)
- 亦称数据类型(Data Type)
var
自动识别变量类型 - 变量是存放数据的地方,简称“数据”
- 变量的声明
- 变量的使用
- 方法(旧称函数)是处理数据的逻辑,又称“算法”
- 方法的声明
- 方法的调用
- 程序=数据+算法
- 有了变量和方法就可以写有意义的程序了
算法简介
循环
递归
详解类型、变量与对象
什么是类型(Type)
- 又名数据类型(Data Type)
- A data type is a homogeneous collection of values, effectively presented, equipped with a set of operations which manipulate these values.
- 是数据在内存中存储时的“型号”
- 小内存容纳大尺寸数据会丢失精确度、发生错误
- 大内存容纳小尺寸数据会导致浪费
- 编程语言的数据类型与数据的数据类型不完全相同
- 强类型语言与弱类型语言的比较
- C语言示例:if条件
- JavaScript示例:动态类型
- C#语言对弱类型/动态类型的模仿
using System;
//C#中的弱类型
namespace Test01
{
class Program
{
static void Main(string[] args)
{
dynamic myVar = 100;
Console.WriteLine(myVar);
myVar = "Mr. Okay";
Console.WriteLine(myVar);
}
}
}
100
Mr. Okay
类型在C#语言中的作用
- 一个C#类型中所包含的信息有:
- 存储此类型变量所需的内存空间大小
- 此类型的值可表示的最大、最小值范围
- 此类型所包含的成员(如方法、属性、事件等)
- 此类型由何基类派生而来
- 程序运行的时候,此类型的变量在分配在内存的什么位置
- Stack简介
- Stack overflow
- Heap简介
- 使用Performance Monitor查看进程的堆内存使用量
- 关于内存泄漏
- 此类型所允许的操作(运算)
using System;
using System.Windows.Forms;
namespace Test01
{
class Program
{
static void Main(string[] args)
{
Type myType = typeof(Form);
Console.WriteLine(myType.FullName);
}
}
}
System.Windows.Forms.Form
Console.WriteLine(myType.BaseType.FullName);
System.Windows.Forms.ContainerControl
栈溢出:
namespace Test01
{
class Program
{
static void Main(string[] args)
{
BadGuy bg = new BadGuy();
bg.BadMethod();
}
}
class BadGuy
{
public void BadMethod()
{
int x = 100;
this.BadMethod();
}
}
}
//允许不安全的代码
namespace Test01
{
class Program
{
static void Main(string[] args)
{
unsafe
{
int* p = stackalloc int[9999999];
}
}
}
}
Process is terminated due to StackOverflowException.
C#语言的类型系统
- C#的五大数据类型
- 类(Class)
class
:如Windows,Form,Console,String - 结构体(Structures)
struct
:如Int32,Int64,Single,Double - 枚举(Enumerations)
enum
:如HorizontalAlignment,Visibility - 接口(Interfaces)
- 委托(Delegates)
- 类(Class)
- C#类型的派生谱系
- Object
- 引用类型(Reference Type)
- 类
- 接口
- 委托
- 值类型(Value Type)
- 结构体
- 枚举
- 引用类型(Reference Type)
- Object
变量、对象与内存
- 什么是变量
- 表面上来看,变量的用途是存储数据
- 实际上,变量表示了存储位置,并且每个变量都有一个类型,以决定什么样的值能够存入变量
- 变量一共有7种
- 静态变量,实例变量(成员变量,字段),数组元素,值参数,引用参数,输出形参,局部变量
- 狭义的变量指局部变量,因为其他种类的变量都有自己的约定名称
- 简单的讲,局部变量就是方法体(函数体)里声明的变量
- 变量的声明
- 有效的修饰符组合:类型、变量名、初始化器
- 值类型的变量
- 以byte/sbyte/short/ushort为例
- 值类型没有实例,所谓的“实例”与变量合而为一
- 引用类型的变量与实例
- 引用类型变量与实例的关系:引用类型变量里存储的数据是对象的内存地址
- 局部变量是在stack上分配内存
- 变量的默认值
- 默认为0
- 本地变量必须有赋值
- 常量(值不可改变的变量)
-
const int x = 100 ```
-
- 装箱与拆箱(Boxing & Unboxing)
==变量 = 以变量名所对应的内存地址为起点、以其数据类型所要求的存储空间为长度的一块内存区域 #EE3F4D==
负数在内存中存储使用的是反码+1
方法的定义、调用与调试
方法的由来
- 方法(method)的前身是C/C++语言中的函数(function)
- 方法是面向对象范畴的概念,在非面向对象语言中任然成为函数
- 使用C/C++作对比
- 永远都是类(或结构体)的成员
- C#语言中函数不可能独立于类(或结构体)之外
- 只有作为类(结构体)的成员时才被称为方法
- C++中是可以的,称为“全局函数”
- 是类(结构体)最基本的成员之一
- 最基本的成员只有两个——字段与方法(成员变量与成员方法),本质还是数据+算法
- 方法表示类(或结构体)“能做什么事情”
- 为什么需要方法和函数
- 目的1:隐藏复杂的逻辑
- 目的2:把大算法分解为小算法
- 目的3:复用(reuse,重用)
方法的声明与调用
- 声明方法的语法详解
- 参见C#语言文档(声明/定义不分家)
- Parameter全称为“formal parameter”
形式上参数,简称“形参” - Parameter是一种变量
- 为方的命名规范
- 大小写规范
- 需要以动词或者动词短语作为名字
- 重温静态(static)方法和实例方法
- 调用方法
- Argument中文C#文档的官方译法为“实际参数”,简称“实参”
可理解为调用方法时的真实事件 - 调用方法时的argument列表要与定义方法时的parameter列表相匹配
- C#是强类型语言,argument是值,parameter是变量,值与变量一定要匹配,不然编译器会报错
- Argument中文C#文档的官方译法为“实际参数”,简称“实参”
构造器
- 构造器(constructor是类型的成员之一)
- 狭义的构造器指的是“实例构造器”(instance constructor)
- 如何调佣构造器
- 声明构造器
- 构造器的内存原理
using System;
namespace Test01
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student(2, "Mr. Okay");
Student stu2 = new Student();
Console.WriteLine(stu.ID );
Console.WriteLine(stu.Name);
Console.WriteLine(stu2.ID);
Console.WriteLine(stu2.Name);
}
}
class Student
{
public Student()
{
this.ID = 1;
this.Name = "No name";
}
public Student(int initID, string initName)
{
this.ID = initID;
this.Name = initName;
}
public int ID;
public string Name;
}
}
2
Mr. Okay
1
No name
VS中输入ctor可快捷生成构造器
方法的重载(Overload)
- 调用重载方法的示例
- 声明带有重载的方法
- 方法签名(method signature)由方法的名称、类型形参的个数和它的每一个形参(按从左到右的顺序)的类型和种类(值、引用或输出)组成。==方法签名不包含返回类型。 #EE3F4D==
- 实例构造函数签名由它的每一个形参(按从左到右的顺序)的类型和种类(值、引用或输出)组成
- 重载决策(到底调用哪一个重载):用于在给定了参数列表和一组候选函数成员的情况下,选择一个最佳函数成员来实施调用。
如何对方法进行debug
- 设置断点(breakpoint)
- 观察方法调用时的call stack
- Step-in, Step-over, Step-out
- 观察局部变量的值与变化
方法的调用与栈
- 方法调用时占内存的分配
- 对stack frame的分析
操作符详解
操作符概览
- 操作符(Operator)也译作“运算符”
- 操作符是用来操作数据的,被操作符操作的数据成为操作数 (Operand)
操作符的本质
- 操作符的本质是函数(即算法)的“简记法”
- 操作符不能脱离与它关联的数据类型
- 可以说操作符就是与固定数据类型相关联的一套基本算法的简记法
优先级与运算数据
- 操作符的优先级
- 可以使用圆括号提高被括起来表达式的优先级
- 圆括号可以嵌套
- 不像数学里有方括号和花括号,在C#语言里“[]”与“{}”有专门的用途
- 同优先级操作符的运算数据
- 除了带有赋值功能的操作符,同优先级操作符都是由左向右进行运算
- 带有赋值功能的操作符的运算顺序是由有向左
- 与数学运算不同,计算机语言的同优先级运算没有“结合律”
int[] myIntArray = new int[] {1 ,2 ,3 ,4 ,5};
TypeOf操作符
using System;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
Type t = typeof(int);
Console.WriteLine(t.Namespace);
Console.WriteLine(t.FullName);
Console.WriteLine(t.Name);
int c = t.GetMethods().Length;
foreach (var mi in t.GetMethods())
{
Console.WriteLine(mi.Name);
}
Console.WriteLine(c);
}
}
}
System
System.Int32
Int32
CompareTo
CompareTo
Equals
Equals
GetHashCode
ToString
ToString
ToString
ToString
Parse
Parse
Parse
Parse
TryParse
TryParse
GetTypeCode
GetType
17
default操作符
using System;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
int x = default(int);
Console.WriteLine(x);
}
}
}
0
using System;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
Form myForm = default(Form);
Console.WriteLine(myForm==null);
}
}
}
True
using System;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
Level level = default(Level);
Console.WriteLine(level);
}
}
enum Level
{
Low,
Mid,
High
}
}
Low
new和var的功能
var person = new {Name = "Mr.Okay", Age = 34 };
new为匿名类型创建对象,var为隐式类型变量引用变量
new功能强大,不能乱用
类型转换
- 隐式(implicit)类型转换
- 不丢失精度的转换
- 子类向父类的转换
- 装箱
- 显式(explicit)类型转换
- 有可能丢失精度(甚至发生错误)的转换,即cast
- 拆箱
- 使用
Convert
类 - ToString方法与各数据类型的Parse/TryParse方法
- 自定义类型转换操作符
using System;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
string str1 = Console.ReadLine();
string str2 = Console.ReadLine();
int x = Convert.ToInt32(str1);
int y = Convert.ToInt32(str2);
Console.WriteLine(x + y);
}
}
}
12
34
46
隐式类型转换
将数值类型转换为字符串类型
this.tb3.Text = System.Convert.ToString(result);
this.tb3.Text = result.ToString();
Parse/TryParse方法
double x = double.Parse(this.tb1.Text);
只能解析格式正确的字符串类型
类型转换操作符
显示类型转换操作符
using System;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
Stone stone = new Stone();
stone.Age = 5000;
Monkey wukongSun = (Monkey)stone;
Console.WriteLine(wukongSun.Age) ;
}
}
class Stone
{
public int Age;
public static explicit operator Monkey(Stone stone)
{
Monkey m = new Monkey();
m.Age = stone.Age / 500;
return m;
}
}
class Monkey
{
public int Age;
}
}
隐式类型转换操作符
using System;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
Stone stone = new Stone();
stone.Age = 5000;
Monkey wukongSun = (Monkey)stone;
Console.WriteLine(wukongSun.Age) ;
}
}
class Stone
{
public int Age;
public static explicit operator Monkey(Stone stone)
{
Monkey m = new Monkey();
m.Age = stone.Age / 500;
return m;
}
}
class Monkey
{
public int Age;
}
}
double a = double.PositiveInfinity;//正无穷大
double b = double.NegativeInfinity;//负无穷大
is:检验一个对象是否是对应类型的对象,结果是bool类型
Nullable<int> x = null;
int? x = null;
??:null值合并操作符
using System;
namespace c.biancheng.net
{
class Demo
{
static void Main(string[] args){
int? num1 = null;
int? num2 = 123;
int num3;
num3 = num1 ?? 321;
Console.WriteLine("num3 = {0}", num3);
num3 = num2 ?? 321;
Console.WriteLine("num3 = {0}", num3);
Console.ReadLine();
}
}
}
表达式,语句详解
表达式的定义
- 什么是表达式
- 表达式是任何一门编程语言的基础组件之一,它有命令和声明。表达式可以说是任何一门编程语言的核心组件。
- 表达式是一种语法实体,它的功能是用来求值,它的结果只有两个——成功和失败。
- 各种编程语言对表达式的视线不尽相同,但大体上都符合这个定义。
- C#语言对表达式的定义
- 算法逻辑的最基本(最小)单元,表达一定的算法意图
- 因为操作符有优先级,所以表达式也就有了优先级
各类表达式概览
- C#语言中表达式的分类
- A value. Every value has an associated type. 任何能得到值的运算(回顾操作符和结果类型)
- A variable. Every variable has an associated type.
- A namespase.
- A Type.
- A method group.例如:Console.WriteLine,这是一组方法,重载决策决定具体调佣哪一个
- A null literal.
- An anonymous function.
- A property access.
- An event access.
- An indexer access.
- Nothing.对返回值为void的方法的调用
- 符合表达式的求值
- 注意操作符的优先级和同优先级操作符的运算方向
- 参考C#语言定义文档
语句的定义
-
语句是高级语言的语法——编译语言和机器语言只有指令(高级语言中的表达式对应低级语言中的指令),==语句等价于一个或一组有明显逻辑关联的指令 #12AA9C==。
-
C#对语句的定义
- C#语言的语句除了能够让程序员“顺序地”(sequentially)表达算法思想,还能通过条件判断、跳转和循环等方法控制程序逻辑的走向
- 简而言之就是:陈述算法思想,控制逻辑走向,完成==有意义的 #EE3F4D==动作(action)
- C#语言的语句由分号
;
结尾,但由分号结尾的不一定都是语句 - 语句一定是出现在方法体里
语句详解
- 标签语句
- 声明语句
- 嵌入式语句
- 声明语句
- 表达式语句
- 块语句(简称“块”)
- 选择(判断、分支)语句
- 迭代(循环)语句
- 跳转语句
- try...catch...finally语句
- using语句
- yield语句
- checked/unchecked语句
- lock语句(用于多线程)
- 标签语句
- 空语句
try...catch...finally语句
using System;
namespace ClassAndInstance
{
class Program
{
static void Main(string[] args)
{
Calculator c = new Calculator();
int r = 0;
try
{
r = c.Add("a", "200");
}
catch(OverflowException oe)
{
Console.WriteLine(oe.Message);
}
Console.WriteLine(r);
}
}
class Calculator
{
public int Add(string arg1, string arg2)
{
int a = 0;
int b = 0;
bool hasError = false;
try
{
a = int.Parse(arg1);
b = int.Parse(arg2);
}
catch (ArgumentNullException ane)
{
Console.WriteLine(ane.Message);
hasError = true;
}
catch (FormatException fe)
{
Console.WriteLine(fe.Message);
hasError = true;
}
catch (OverflowException oe)
{
//Console.WriteLine(oe.Message);
//hasError = true;
throw oe;
}
finally
{
if (hasError)
{
Console.WriteLine("Extcution has error");
}
else
{
Console.WriteLine("Done!");
}
}
int result = checked(a + b);
return result;
}
}
}
迭代语句
foreach语句
foreach (var item in collection)
{
}
字段,属性,索引器,常量
字段
- 什么是字段
- 字段(field)是一种表示与对象或类型(类与结构体)关联的变量
- 字段是类型的成员,旧称“成员变量”
- 与对象关联的字段亦称为“实例字段”
- 与类型关联的字段称为”静态字段“,由static修饰
- 字段的声明
- 参见C#语言定义文档
- 尽管字段声明带分号,但它不是语句
- 字段的名字一定是名词
- 字段的初始值
- 无显式初始化时,字段获得其类型的默认值,所以字段”永远都不会未被初始化“
- 实例字段初始化的时机——对象创建时
- 静态字段初始化的时机——类型被加载(load)时
- 只读字段
- 实例只读字段
- 静态只读字段
属性
- 什么是属性
- 属性(Property)是一种用于访问对象或类型的特征的成员,特征反映了形态
- 属性是字段的自然扩展
- 从命名上看,field跟更偏向于实例对象在内存中的布局,Property更偏向于反映现实世界对象的特征
- 对外:暴露数据,数据可以是存储在字段里的,也可以是动态计算出来的
- 对内:保护字段不被非法值“污染”
- 属性由Get/Set方法对进化而来
- 又一个“语法糖”——属性背后的秘密
- 属性的声明
- 完整声明——后台(back)成员变量与访问器(注意使用code snippet和refactor工具)
- 简略声明——只有访问器(查看IL代码)
- 动态计算值的属性
- 注意实例属性和静态属性
- 属性的名字一定是名词
- 只读属性——只有getter没有setter
- 尽管语法上正确,几乎没有人使用“只写属性”,因为属性的主要目的是通过向外暴露数据而展示对象/类型的状态
- 属性与字段的关系
- 一般情况下,它们都用于表示实体(对象或类型)的状态
- 属性大多数情况下是字段的包装器(wrapper)
- 建议:永远使用属性(而不是字段)来暴露数据,即字段永远都是private或protected的
propfull
快速生成完整属性
prop
生成简略属性
Ctrl+R+E同理
using System;
namespace Text3
{
class Program
{
static void Main(string[] args)
{
try
{
Student stu1 = new Student();
stu1.Age = 20;
Student stu2 = new Student();
stu2.Age = 20;
Student stu3 = new Student();
stu3.Age = 20;
int avgAge = (stu1.Age + stu2.Age + stu3.Age) / 3;
Console.WriteLine(avgAge);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
class Student
{
private int age;
public int Age
{
get
{
return this.age;
}
set
{
if (value >= 0 && value <= 120)
{
this.age = value;
}
else
{
throw new Exception("Age value has error");
}
}
}
public int GetAge()
{
return this.age;
}
public void SetAge(int value)
{
if(value >= 0 && value <= 120)
{
this.age = value;
}
else
{
throw new Exception("Age value has error.");
}
}
}
}
索引器(概述)
- 什么是索引器
- 索引器(indexer)是这样一种成员:它使对象能够用与数组相同的方式(即使用下标)进行索引
- 索引器的声明
- 参见C#语言定义文档
- 注意:没有静态索引器
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student();
stu["Math"] = 90;
stu["Math"] = 100;
var mathScore = stu["Math"];
Console.WriteLine(mathScore);
}
}
class Student
{
private Dictionary<string, int> scoreDictionary = new Dictionary<string, int>();
public int? this[string subject]
{
get
{
if (this.scoreDictionary.ContainsKey(subject))
{
return this.scoreDictionary[subject];
}
else
{
return null;
}
}
set
{
if (value.HasValue == false)
{
throw new Exception("Score can not be null");
}
if (this.scoreDictionary.ContainsKey(subject))
{
this.scoreDictionary[subject] = value.Value;
}
else
{
this.scoreDictionary.Add(subject, value.Value);
}
}
}
}
}
常量const
- 什么是常量
- 常量(constant)是表示常量值(即,可以在编译时计算的值)的类成员
- 常量隶属于类型而不是对象,即没有“实例常量”
- “实例常量”的角色由只读实例字段来担当
- 注意区分成员常量与局部常量
- 常量的声明
- 各种“==只读 #12AA9C==”的应用场景
- 为了提高程序可读性和执行效率——常量
- 为了防止对象的值被改变——只读字段
- 向外暴露不允许修改的数据——只读属性(静态或非静态),功能与常量有一些重叠
- 当希望成为常量的值其类型不能被常量声明接受时(类/自定义结构体)——静态只读字段
转义符
转义序列 | 含义 |
---|---|
\\ | \字符 |
\' | '字符 |
\" | "字符 |
\? | ?字符 |
\a | Alert或bell |
\b | 退格键(Backspace) |
\f | 换页符(From feed) |
\n | 换行符(Newline) |
\r | 回车 |
\t | 水平制表符tab |
\v | 垂直制表符tab |
\ooo | 一到三位的八进制字符 |
\xhh... | 一个或多个数字的十六进制字符 |
传值/输出/引用/数组/具名/可选参数,扩展方法
传值参数
传值参数→值类型
- 注意
- 值参数创建变量的副本
- 对值参数的操作永远不影响变量的值
传值参数→引用类型,并且新创建对象
using System;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Student stu = new Student() { Name = "Tim" };
SomeMethod(stu);
Console.WriteLine(stu.Name);
}
static void SomeMethod(Student stu)
{
stu = new Student() { Name = "Tom" };
Console.WriteLine(stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
}
Tom
Tim
传值参数→引用类型,只操作对象,不创建新对象
- 注意
- 对象还是那个对象,但对象里的值(字段/属性)已经改变
引用参数
引用参数→值类型
- 注意
- 使用参数并不创建变量的副本
- 使用ref修饰符显式指出——此方法的副作用是改变实际参数的值
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
int y = 1;
IWantSideEffect(ref y);
Console.WriteLine(y);
}
static void IWantSideEffect(ref int x)
{
x = x + 100;
}
}
}
引用参数→引用类型,创建新对象
- 注意
- 使用参数并不创建变量的副本
- 使用ref修饰符显式指出——此方法的副作用是改变实际参数的值
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Student otterStu = new Student() { Name = "Tim" };
Console.WriteLine("Hashcode={0}, Name={1}", otterStu.GetHashCode(), otterStu.Name);
Console.WriteLine("------------------------");
IWantSideEffect(ref otterStu);
Console.WriteLine("Hashcode={0}, Name={1}", otterStu.GetHashCode(), otterStu.Name);
}
static void IWantSideEffect(ref Student stu)
{
stu = new Student() { Name = "Tom" };
Console.WriteLine("Hashcode={0}, Name={1}",stu.GetHashCode(),stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
}
Hashcode=46104728, Name=Tim
------------------------
Hashcode=12289376, Name=Tom
Hashcode=12289376, Name=Tom
引用参数→引用类型,不创建新对象只改变对象值
- 注意
- 此时与传值参数在效果上并无不同,但机理不一样,有些面试题会考到
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Student otterStu = new Student() { Name = "Tim" };
Console.WriteLine("HashCode={0},Name={1}", otterStu.GetHashCode(), otterStu.Name);
Console.WriteLine("--------------------------");
SomeSideEffect(ref otterStu);
Console.WriteLine("HashCode={0},Name={1}", otterStu.GetHashCode(), otterStu.Name);
}
static void SomeSideEffect(ref Student stu)
{
stu.Name = "Tom";
Console.WriteLine("HashCode={0},Name={1}",stu.GetHashCode(), stu.Name);
}
}
class Student
{
public string Name { get; set; }
}
}
Hashcode=46104728, Name=Tim
------------------------
Hashcode=12289376, Name=Tom
Hashcode=12289376, Name=Tom
输出参数
输出参数→值类型
- 注意
- 输出参数并不创建变量的副本
- 方法体内必须要有对输出变量的赋值的操作
- 使用out修饰符显式指出——此方法的副作用是通过参数向外输出值
- 从语义上来讲——ref是为了“改变”,out是为了“输出”
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please input first number:");
string arg1 = Console.ReadLine();
double x = 0;
bool b1 = double.TryParse(arg1, out x);
if (b1 == false)
{
Console.WriteLine("Input error!");
return;
}
Console.WriteLine("Please input second number:");
string arg2 = Console.ReadLine();
double y = 0;
bool b2 = double.TryParse(arg2, out y);
if (b2 == false)
{
Console.WriteLine("Input error!");
return;
}
double z = x + y;
Console.WriteLine("{0}+{1}={2}", x, y, z);
}
}
}
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
double x = 0;
bool b = DoubleParser.TryParse("ABC", out x);
if (b == true)
{
Console.WriteLine(x + 1);
}
else
{
Console.WriteLine(x);
}
}
}
class DoubleParser
{
public static bool TryParse(string input, out double result)
{
try
{
result = double.Parse(input);
return true;
}
catch
{
result = 0;
return false;
}
}
}
}
输出参数→引用类型
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Student stu = null;
bool b = StudentFactory.Create("Tim", 34, out stu);
if (b == true)
{
Console.WriteLine("Student {0}, age is {1}",stu.Name, stu.Age);
}
}
}
class Student
{
public int Age { get; set; }
public string Name { get; set; }
}
class StudentFactory
{
public static bool Create(string stuName, int stuAge, out Student result)
{
result = null;
if (string.IsNullOrEmpty(stuName))
{
return false;
}
if(stuAge<20 || stuAge > 80)
{
return false;
}
result = new Student() { Name = stuName, Age = stuAge };
return true;
}
}
}
数组参数
- 必须是形参列表中的最后一个,由params修饰
- 举例:String.Format方法和String.Split方法
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
int result = CalculateSum(1,2,3);
Console.WriteLine(result);
}
static int CalculateSum(params int[] intArray)
{
int sum = 0;
foreach (var item in intArray)
{
sum += item;
}
return sum;
}
}
}
string中的split
方法
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
string str = "Tim,Tom;Amy.Lisa";
string[] result = str.Split(',', ';', ',', '.');
foreach (var name in result)
{
Console.WriteLine(name);
}
}
}
}
具名参数
- 参数的位置不受约束
可选参数
- 参数因为具有默认值而变得“可选”
- 不推荐使用可选参数
扩展方法(this参数)
- 方法必须是公有、静态的,即被public static所修饰
- 必须是形参列表中的第一个,被this修饰
- 必须由一个静态类(一般类名为SomeTypeExtension)来统一收纳对SomeType类型的扩展方法
- 举例:LINQ方法
using System;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
double x = 3.14159;
double y = x.Round(4);
Console.WriteLine(y);
}
}
static class DoubleExtension
{
public static double Round(this double input, int digits)
{
double result = Math.Round(input, digits);
return result;
}
}
}
委托详解
什么是委托
- 委托(delegate)是函数指针的“升级版”
- 实例:C/C++中的函数指针
- 一切皆地址
- 变量(数据)是以某个地址为起点的一段内存中所存储的值
- 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
- 直接调用与间接调用
- 直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行→返回
- 间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行→返回
- Java中没有与委托相对应的功能实体
- 委托的简单使用
- Action委托
- Func委托
using System;
namespace DelegateExample
{
class Program
{
static void Main(string[] args)
{
Calculator calculator = new Calculator();
Action action = new Action(calculator.Report);
calculator.Report();
action.Invoke();
action();
Func<int, int, int> func1 = new Func<int, int, int>(calculator.Add);
Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub);
int x = 100;
int y = 200;
int z = 0;
z = func1(x, y);
Console.WriteLine(z);
z = func2.Invoke(x, y);
Console.WriteLine(z);
}
}
class Calculator
{
public void Report()
{
Console.WriteLine("I have 3 methods");
}
public int Add(int a, int b)
{
int result = a + b;
return result;
}
public int Sub(int a, int b)
{
int result = a - b;
return result;
}
}
}
委托的声明(自定义委托)
- 委托是一种类(class),类是数据类型所以委托也是一种数据类型
- 它的声明方式与一般的类不同,主要是为了照顾可读性和C/C++传统
- 注意声明委托的位置
- 避免写错地方结果声明成嵌套类型
- 委托与所封装的方法必须“类型兼容”
- 返回值的数据类型一致
- 参数列表在个数和数据类型上一致(参数名不需要一样)
using System;
namespace DelegateExample
{
public delegate double Calc(double x, double y);
class Program
{
static void Main(string[] args)
{
Calculator calculator = new Calculator();
Calc calc1 = new Calc(calculator.Add);
Calc calc2 = new Calc(calculator.Sub);
Calc calc3 = new Calc(calculator.Mul);
Calc calc4 = new Calc(calculator.Div);
double a = 100;
double b = 200;
double c = 0;
c = calc1(a, b);
Console.WriteLine(c);
c = calc2(a, b);
Console.WriteLine(c);
c = calc3(a, b);
Console.WriteLine(c);
c = calc4(a, b);
Console.WriteLine(c);
}
}
class Calculator
{
public double Add(double x, double y)
{
return x + y;
}
public double Sub(double x, double y)
{
return x - y;
}
public double Mul(double x, double y)
{
return x * y;
}
public double Div(double x, double y)
{
return x / y;
}
}
}
委托的一般使用
- 实例:把方法当做参数传给另一个方法
- 正确使用1:==模板方法 #EE3F4D==,“借用”指定的外部方法来产生结果
- 相当于“填空题”
- 常位于代码中部
- 委托有返回值
- 正确使用2:==回调(callback)方法 #EE3F4D==,调用指定的外部方法
- 相当于“流水线”
- 常位于代码末尾
- 委托无返回值
- 正确使用1:==模板方法 #EE3F4D==,“借用”指定的外部方法来产生结果
- 注意:难精通+易使用+功能强大的东西,一旦被滥用则后果非常严重
- 缺点1:这是一种方法级别的紧耦合,现实工作中要慎之又慎
- 缺点2:使可读性下降、debug的难度增加
- 缺点3:把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
- 缺点4:委托使用不当有可能造成内存泄漏和程序性能下降
模板方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DelegateExample
{
class Program
{
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();
WrapFactory wrapFactory = new WrapFactory();
Func<Product> fun1 = new Func<Product>(productFactory.MakePizza);
Func<Product> fun2 = new Func<Product>(productFactory.MakeToyCar);
Box box1 = wrapFactory.WrapProduct(fun1);
Box box2 = wrapFactory.WrapProduct(fun2);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
}
}
class Product
{
public string Name { get; set; }
}
class Box
{
public Product Product { get; set; }
}
class WrapFactory
{
public Box WrapProduct(Func<Product> getProduct)
{
Box box = new Box();
Product product = getProduct.Invoke();
box.Product = product;
return box;
}
}
class ProductFactory
{
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizza";
return product;
}
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "Toy Car";
return product;
}
}
}
回调方法
using System;
namespace DelegateExample
{
class Program
{
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();
WrapFactory wrapFactory = new WrapFactory();
Func<Product> fun1 = new Func<Product>(productFactory.MakePizza);
Func<Product> fun2 = new Func<Product>(productFactory.MakeToyCar);
Logger logger = new Logger();
Action<Product> log = new Action<Product>(logger.Log);
Box box1 = wrapFactory.WrapProduct(fun1, log);
Box box2 = wrapFactory.WrapProduct(fun2, log);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
}
}
class Logger
{
public void Log(Product product)
{
Console.WriteLine("Product '{0}' created at {1}. Price is {2}.", product.Name, DateTime.UtcNow, product.Price);
}
}
class Product
{
public string Name { get; set; }
public double Price { get; set; }
}
class Box
{
public Product Product { get; set; }
}
class WrapFactory
{
public Box WrapProduct(Func<Product> getProduct, Action<Product> logCallback)
{
Box box = new Box();
Product product = getProduct.Invoke();
if (product.Price>=50)
{
logCallback(product);
}
box.Product = product;
return box;
}
}
class ProductFactory
{
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizza";
product.Price = 12;
return product;
}
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "Toy Car";
product.Price = 100;
return product;
}
}
}
委托的高级使用
- 多播(multicast)委托
- 隐式异步调用
- 同步与异步的简介
- 中英文的语言差异
- 同步:你做完了我(在你的基础上)接着做
- 异步:咱们两个同时做(相当于汉语中的“同步进行”)
- 同步调用与异步调用的对比
- 每一个运行的程序是一个进程(process)
- 每个进程可以有一个或者多个线程(thread)
- 同步调用是在同一线程内
- 异步调用的底层机理是==多线程 #EE3F4D==
- 串行==同步==单线程,并行==异步==多线程
- 隐式多线程 v.s.显式多线程
- 直接同步调用:使用方法名
- 简介同步调用:使用单播/多播委托的Invoke方法
- 隐式异步调用:使用委托的BeginInvoke
- 显式异步调用:使用Thread或Task
- 同步与异步的简介
- 应该适时地使用接口(interface)取代一些对委托的使用
- Java完全地使用接口取代了委托的功能,即Java没有与C#中委托相对性的功能实体
多播委托
using System;
using System.Threading;
namespace MulticastDelegateExample
{
class Program
{
static void Main(string[] args)
{
Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
Action action1 = new Action(stu1.DoHomework);
Action action2 = new Action(stu2.DoHomework);
Action action3 = new Action(stu3.DoHomework);
action1 += action2;
action1 += action3;
action1.Invoke();
}
}
class Student
{
public int ID { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomework()
{
for (int i = 0; i < 5; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);
Thread.Sleep(1000);
}
}
}
}
隐式异步调用
using System;
using System.Threading;
namespace MulticastDelegateExample
{
class Program
{
static void Main(string[] args)
{
Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
Action action1 = new Action(stu1.DoHomework);
Action action2 = new Action(stu2.DoHomework);
Action action3 = new Action(stu3.DoHomework);
action1.BeginInvoke(null, null);
action2.BeginInvoke(null, null);
action3.BeginInvoke(null, null);
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread{0}.", i);
Thread.Sleep(1000);
}
}
}
class Student
{
public int ID { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomework()
{
for (int i = 0; i < 5; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);
Thread.Sleep(1000);
}
}
}
}
显式异步调用
使用thread
进行显式异步调用
using System;
using System.Threading;
namespace MulticastDelegateExample
{
class Program
{
static void Main(string[] args)
{
Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework));
Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework));
Thread thread3 = new Thread(new ThreadStart(stu3.DoHomework));
thread1.Start();
thread2.Start();
thread3.Start();
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread{0}.", i);
Thread.Sleep(1000);
}
}
}
class Student
{
public int ID { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomework()
{
for (int i = 0; i < 5; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);
Thread.Sleep(1000);
}
}
}
}
使用task
进行显式异步调用
using System;
using System.Threading;
using System.Threading.Tasks;
namespace MulticastDelegateExample
{
class Program
{
static void Main(string[] args)
{
Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };
Task task1 = new Task(new Action(stu1.DoHomework));
Task task2 = new Task(new Action(stu2.DoHomework));
Task task3 = new Task(new Action(stu3.DoHomework));
task1.Start();
task2.Start();
task3.Start();
for (int i = 0; i < 10; i++)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Main thread{0}.", i);
Thread.Sleep(1000);
}
}
}
class Student
{
public int ID { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomework()
{
for (int i = 0; i < 5; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hour(s).",this.ID, i);
Thread.Sleep(1000);
}
}
}
}
事件详解
初步了解事件
- ==定义 #EE3F4D==:单词Event,译为“事件”
- 《牛津词典》中的解释是"a thing that happens, espacially something important"
- 通顺的解释就是“====能够发生的什么事情 #FC7930== #66ccff==”
- ==角色 #EE3F4D==:使对象或类具备==通知能力 #FC7930==的成员
- (中译)事件(event)是一种使对象或类能够提供通知的成员
- (原文)An event is a member that enables an object or class to provide notifications.
- “对象O拥有一个事件E”想表达的思想是:当事件E发生的时候,O有能力通知别的对象
-
事件的功能 = 通知 + 可选的事件参数(即详细信息)
- ==使用 #EE3F4D==:用于对象或类间的动作协调与信息传递(消息推送)
- ==原理 #EE3F4D==:事件模型(event model)中的两个“5”
- “==发生→响应 #EE3F4D==”中的5个部分——闹钟响了你起床、孩子饿了你做饭......这里隐含着“订阅”关系
- “==发生→响应 #EE3F4D==”中的5个动作——(1)我有一个事件→(2)一个人或者一群人关心我的这个事件→(3)我的这个事件发生了→(4)关心这个事件的人会被依次通知到→(5)被通知到的人根据拿到的事件信息(又称“事件数据”、“事件参数”、“通知”)对事件进行相应(又称“处理事件”)。
- ==提示 #EE3F4D==
- 事件多用于桌面、手机等开发的客户端编程,因为这些程序经常是用户通过事件来“驱动”的
- 各种编程语言对这个机制的实现方法不尽相同
- Java语言里没有事件这种成员,也没有委托这种数据类型。Java的“事件”是使用接口来实现的
- MVC、MVP、MVVM等默示,是事件模式更高级、更有效的“玩法”
- 日常开发的时候,使用已有事件的机会比较多,自己声明事件的机会比较少,所以先学使用
事件的应用
- 实例演示
- 派生(继承)与扩展(extends)
- 事件模型的五个组成部分
- 事件的拥有者(event source, 对象)
- 事件成员(event,成员)
- 事件的响应者(event subscriber,对象)
- 事件处理器(event handler,成员)——本质上是一个回调方法
- 事件订阅——把事件处理器与事件关联在一起,本质上是一种以委托类型为基础的“约定”
- 注意
- 事件处理器是方法成员
- 挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是个“语法糖”
- 事件处理器对事件的订阅不是随意的,匹配是否由声明事件时所使用的委托类型来检测
- 事件可以同步调用也可以异步调用
事件不会主动发生,一定会是拥有者的内部逻辑触发才会发生
事件订阅操作符:+=
using System;
using System.Timers;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
Timer timer = new Timer();
timer.Interval = 1000;
Boy boy = new Boy();
Girl girl = new Girl();
timer.Elapsed += boy.Action;
timer.Elapsed += girl.Action;
timer.Start();
Console.ReadLine();
}
}
class Boy
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Jump!");
}
}
class Girl
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Sing!");
}
}
}
using System;
using System.Windows.Forms;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
Form form = new Form();
Controller controller = new Controller(form);
form.ShowDialog();
}
}
class Controller
{
private Form form;
public Controller(Form form)
{
if (form!=null)
{
this.form = form;
this.form.Click += this.FormClicked;
}
}
private void FormClicked(object sender, EventArgs e)
//也就是说,你不能拿影响Elapsed事件的事件处理器去响应Click事件——因为遵循的约束不同,所以他们是不通用的
{
this.form.Text = DateTime.Now.ToString();
}
}
}
using System;
using System.Windows.Forms;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
MyForm form = new MyForm();
form.Click += form.FormClicked;
form.ShowDialog();
}
}
class MyForm : Form
{
internal void FormClicked(object sender, EventArgs e)
{
this.Text = DateTime.Now.ToString();
}
}
}
using System;
using System.Windows.Forms;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
MyForm form = new MyForm();
form.ShowDialog();
}
}
class MyForm : Form//事件的响应者
{
private TextBox textBox;
private Button button;//事件的拥有者
public MyForm()
{
this.textBox = new TextBox();
this.button = new Button();
this.Controls.Add(this.button);
this.Controls.Add(this.textBox);
this.button.Click/*事件*/ += this.ButtonClicked;//事件的订阅
this.button.Text = "Say Hello.";
this.button.Top = 100;
}
private void ButtonClicked(object sender, EventArgs e)//事件处理器
{
this.textBox.Text = "Hello World!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
}
}
}
事件的声明
- 事件的声明
- 完整声明
- 简略声明(字段式声明,field-==like #FC7930==)
- 有了委托字段/属性,为什么还需要事件?
- 为了程序的逻辑更加“有道理”,更加安全,==谨防“借刀杀人” #FC7930==
- 所以==事件的本质 #FC7930==是委托字段的一个包装器
- 这个包装器对委托字段的访问起==限制作用 #FC7930==,相当于一个“蒙版”
- 封装(encapsulation)的一个重要功能就是隐藏
- 事件==对外界 #FC7930==隐藏了委托实例的大部分功能,==仅暴露添加/移除事件处理器的功能 #FC7930==
- 添加/移除事件处理器的时候可以直接使用方法名,这是委托实例所不具备的功能
- 用于声明事件的委托类型的命名约定
- 用于声明Foo事件的委托,一般命名为FooEventHandler(除非是一个非常通用的事件约束)
- FooEventHandler委托的参数一般有两个(由Win32 API演化而来,历史悠久)——
- 第一个是object类型,名字为sender,实际上就是事件的拥有者、事件的source
- 第二个是EventArgs类的派生类,类名一般为FooEventArgs,参数名为e。也就是前面讲过的事件参数
- 虽然没有官方的说法,但我们可以把委托的参数列表看作是事件发生后发送给事件响应者的“==事件消息 #FC7930==”
- 触发Foo事件的方法一般命名为OnFoo,即“因何引发”、“==事出有因 #FC7930==”
- 访问级别为protected,不能为public,不然又成了可以“借刀杀人”了
- 事件的命名约定
- 带有时态的动词或者动词短语
- 事件拥有者“正在做”什么事情,用进行时;时间拥有者“做完了”什么事情,用完成时
完整格式
using System;
using System.Threading;
namespace EventExample
{
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs : EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
public class Customer
{
private OrderEventHandler overEventHandler;
public event OrderEventHandler Order
{
add
{
this.overEventHandler += value;
}
remove
{
this.overEventHandler -= value;
}
}
public double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("I will pay ${0}.", this.Bill);
}
public void WalkIn()
{
Console.WriteLine("Walk into ths restaruant.");
}
public void SitDown()
{
Console.WriteLine("Sit down.");
}
public void Think()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Let me think ... ");
Thread.Sleep(1000);
}
if(this.overEventHandler != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "Kongpao Chicken";
e.Size = "large";
this.overEventHandler.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter
{
public void Action(Customer customer, OrderEventArgs e)
{
Console.WriteLine("I will serve you the dish, {0}", e.DishName);
double price = 10;
switch (e.Size)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
customer.Bill += price;
}
}
}
Walk into ths restaruant.
Sit down.
Let me think ...
Let me think ...
Let me think ...
Let me think ...
Let me think ...
I will serve you the dish, Kongpao Chicken
I will pay $15.
简略声明
using System;
using System.Threading;
namespace EventExample
{
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs : EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
public class Customer
{
public event OrderEventHandler Order;//声明委托
public double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("I will pay ${0}.", this.Bill);
}
public void WalkIn()
{
Console.WriteLine("Walk into ths restaruant.");
}
public void SitDown()
{
Console.WriteLine("Sit down.");
}
public void Think()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Let me think ... ");
Thread.Sleep(1000);
}
if(this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "Kongpao Chicken";
e.Size = "large";
this.Order.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter
{
public void Action(Customer customer, OrderEventArgs e)
{
Console.WriteLine("I will serve you the dish, {0}", e.DishName);
double price = 10;
switch (e.Size)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
customer.Bill += price;
}
}
}
使用自带的EventHandler进行委托
using System;
using System.Threading;
namespace EventExample
{
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs : EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
//public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
public class Customer
{
public event EventHandler Order;//声明委托
public double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("I will pay ${0}.", this.Bill);
}
public void WalkIn()
{
Console.WriteLine("Walk into ths restaruant.");
}
public void SitDown()
{
Console.WriteLine("Sit down.");
}
public void Think()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Let me think ... ");
Thread.Sleep(1000);
}
if(this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "Kongpao Chicken";
e.Size = "large";
this.Order.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter
{
public void Action(object sender, EventArgs e)
{
Customer customer = sender as Customer;
OrderEventArgs orderInfo = e as OrderEventArgs;
Console.WriteLine("I will serve you the dish, {0}", orderInfo.DishName);
double price = 10;
switch (orderInfo.Size)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
customer.Bill += price;
}
}
}
触发Foo事件的方法OnFoo
using System;
using System.Threading;
namespace EventExample
{
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
Waiter waiter = new Waiter();
customer.Order += waiter.Action;
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs : EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
//public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
public class Customer
{
public event EventHandler Order;//声明委托
public double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("I will pay ${0}.", this.Bill);
}
public void WalkIn()
{
Console.WriteLine("Walk into ths restaruant.");
}
public void SitDown()
{
Console.WriteLine("Sit down.");
}
public void Think()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("Let me think ... ");
Thread.Sleep(1000);
}
this.OnOrder("Kongpao Chicken", "large");
}
protected void OnOrder(string dishName, string size)
{
if(this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = dishName;
e.Size = size;
this.Order.Invoke(this, e);
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter
{
public void Action(object sender, EventArgs e)
{
Customer customer = sender as Customer;
OrderEventArgs orderInfo = e as OrderEventArgs;
Console.WriteLine("I will serve you the dish, {0}", orderInfo.DishName);
double price = 10;
switch (orderInfo.Size)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
customer.Bill += price;
}
}
}
事件与委托的关系
- 事件真的是“以特殊方式声明的委托字段/实例”吗?
- ==不是! #EE3F4D==只是声明的时候“看起来像”(对比委托字段与事件的简化声明,field-like)
- 事件声明的时候使用了委托类型,简化声明造成事件看上去像一个委托的字段(实例),而event关键字则更像是一个修饰符——这就是==错觉的来源之一 #FC7930==
- 订阅事件的时候+=操作符后面可以是一个委托实例,这与委托实例的赋值方法语法相同,这也让事件看起来像是一个委托字段——这是==错觉的又一来源 #FC7930==
- 重申:事件的本质是加装在委托字段上的一个“蒙版”(mask),是个起掩蔽作用的包装器。这个用于阻挡非法操作的“蒙版”绝不是委托字段本身
- 为什么要使用委托类型来声明事件?
- 站在source的角度来看,是为了表明source能对外传递哪些消息
- 站在subscriber的角度来看,它是一种约定,是为了约束能够使用什么样签名的方法来处理(响应)事件
- 委托类型的实力将用于(引用)事件处理器
- 对比事件与属性
- 属性不是字段——==很多时候 #EE3F4D==属性是字段的包装器,这个包装器用来保护字段不被滥用
- 事件不是委托字段——它是委托字段的包装器,这个包装器用来保护委托字段不被滥用
- 包装器永远都不可能会是被包装的东西
泛型
- 泛型(generic)无处不在
- 为什么需要泛型:避免成员膨胀或者类型膨胀
- 正交性:泛型类型(类/借口/委托/......)、泛型成员(属性/方法/字段/......)
- 类型方法的参数腿短
- 泛型与委托、lambda表达式
using System;
namespace HelloGeneric
{
class Program
{
static void Main(string[] args)
{
Apple apple = new Apple() { Color = "red" };
Book book = new Book() { Name = "New Book" };
Box<Apple> box1 = new Box<Apple>() { Cargo = apple };
Box<Book> box2 = new Box<Book>() { Cargo = book };
Console.WriteLine(box1.Cargo.Color);
Console.WriteLine(box2.Cargo.Name);
}
}
class Apple
{
public string Color { get; set; }
}
class Book
{
public string Name { get; set; }
}
class Box<TCargo/*类型参数*/>
{
public TCargo Cargo { get; set; }
}
}
泛型接口
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using System.IO;
using System.Runtime.Hosting;
namespace ConsoleApplication13
{
internal class Program
{
static void Main(string[] args)
{
Student<ulong> stu = new Student<ulong>();
stu.ID = 10000000000000000001;
stu.Name = "Tim";
}
}
interface IUnique<TId>
{
TId ID { get; set; }
}
class Student<TId> : IUnique<TId>
{
public TId ID { get; set; }
public string Name { get; set; }
}
}
委托,Lambda,LINQ串讲
委托最基本的用法
using System;
namespace Combine
{
class Program
{
static void Main(string[] args)
{
MyDele dele1 = new MyDele(M1);
dele1.Invoke();
}
static void M1()
{
Console.WriteLine("M1 is called!");
}
}
delegate void MyDele();
}
using System;
namespace Combine
{
class Program
{
static void Main(string[] args)
{
MyDele dele1 = new MyDele(M1);
dele1 += (new Student()).SayHello;
dele1();
}
static void M1()
{
Console.WriteLine("M1 is called!");
}
}
class Student
{
public void SayHello()
{
Console.WriteLine("Hello, I'm a student!");
}
}
delegate void MyDele();
}
间接调用包裹在里面的函数
using System;
namespace Combine
{
class Program
{
static void Main(string[] args)
{
MyDele dele = new MyDele(Add);
int res = dele(100, 200);
Console.WriteLine(res);
}
static int Add(int x, int y)
{
return x + y;
}
}
delegate int MyDele(int a, int b);
}
泛型委托
using System;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
MyDele<int> deleAdd = new MyDele<int>(Add);
int res = deleAdd(100, 200);
Console.WriteLine(res);
MyDele<double> deleMul = new MyDele<double>(Mul);
double mulRes = deleMul(3.0, 4.0);
Console.WriteLine(mulRes);
}
static int Add(int x, int y)
{
return x + y;
}
static double Mul(double x, double y)
{
return x * y;
}
}
delegate T MyDele<T>(T a, T b);
}
.NET Framework声明好的委托
Action
using System;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
Action action = new Action(M1);
action();
var action1 = new Action<string, int>(SayHello);
action1("Tim", 5);
}
static void M1()
{
Console.WriteLine("M1 is called.");
}
static void SayHello(string name1, int round)
{
for (int i = 0; i < round; i++)
{
Console.WriteLine($"Hello, {name1}!");
}
}
}
}
Func
using System;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
Func<int, int, int> func = new Func<int, int, int>(Add);
int res = func(100, 200);
Console.WriteLine(res);
var func2 = new Func<double, double, double>(Mul);
double res2 = func2(3.0, 4.0);
Console.WriteLine(res2);
}
static int Add(int x, int y)
{
return x + y;
}
static double Mul(double x, double y)
{
return x * y;
}
}
}
Lambda表达式
作用
- 匿名方法
- Inline方法
using System;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
//完整形式
//Func<int, int, int> func = new Func<int, int, int>((int a, int b) => { return a + b; });
//int res = func(100, 200);
//Console.WriteLine(res);
Func<int, int, int> func = (a, b) => { return a + b; };
int res = func(100, 200);
Console.WriteLine(res);
func = (x, y) => { return x * y; };
res = func(3, 4);
Console.WriteLine(res);
}
}
}
using System;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
DoSomeCalc((a, b) => { return a * b; }, 100, 200);//泛型委托的类型推断
}
static void DoSomeCalc<T>(Func<T, T, T> func, T x, T y)
{
T res = func(x, y);
Console.WriteLine(res);
}
}
}
LINQ
LINQ: .NET Language Intergrated Query
类
- public:公共的,所有对象都可以访问,但是需要引用命名空间;
- private:私有的,类的内部才可以访问;
- internal:内部的,同一个程序集的对象可以访问,程序集就是命名空间;
- protected:受保护的,类的内部或类的父类和子类中可以访问;
- Protected internal:protected 和 internal 的并集,符合任意一条都可以访问。
什么是类(Class)
- 是一种数据结构(data structure)
- 是一种数据类型(引用类型)
- 代表现实世界中的“种类”
构造器与析构器ctor
- 实例
- 静态
类的声明,继承和访问控制
类的继承,类成员访问
- 类的继承
- 类在功能上的扩展(extend)
- 只能有一个基类,但可以实现多个其接口
- 类访问级别对继承的影响
- 子类的访问级别不能大于父类
- sealed类不能被继承
- 成员的继承与访问
- 派生类对继承成员的访问
- 派生类对积累成员的访问
- 构造器的不可继承性
- 面向对象的实现风格
- Class-based
- Prototype-based
是一个 is a
一个派生类的实例从语义上来说也是基类的实例
重写,多态
- 类的继承
- 类成员的“横向扩展”(成员越来越多)
- 类成员的“纵向扩展”(行为改变,版本增高)
- 类成员的隐藏(不常用)
- 重写与隐藏的发生条件:函数成员,可见,签名一致
- 多态(polymorphism)
- 基于重写机制(virtual→override)
- 函数成员的具体行为(版本)由对象决定
- 回顾:C#语言的变量和对象都是有类型的,所以会有“代差”
using System;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
Vehicle v = new Car();
v.Run();
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running");
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("Car is running");
}
}
}
using System;
namespace ConsoleApplication8
{
class Program
{
static void Main(string[] args)
{
RaseCar rc = new RaseCar();
}
}
class Vehicle
{
private int _speed;
public virtual int Speed
{
get { return _speed; }
set { _speed = value; }
}
public virtual void Run()
{
Console.WriteLine("I'm running");
_speed = 100;
}
}
class Car : Vehicle
{
private int _rpm;
public override int Speed
{
get
{
return _rpm/100;
}
set
{
_rpm = value * 100;
}
}
public override void Run()
{
Console.WriteLine("Car is running");
_rpm = 10000;
}
}
class RaseCar : Car
{
public override void Run()
{
Console.WriteLine("Race car is running!");
}
}
}
接口和抽象类
- 什么是接口和抽象类
- 接口和抽象类都是“软件工程产物”
- 具体类→抽象类→接口:越来越抽象,内部实现的东西越来越少
- 抽象类是未完全实现逻辑的类(可以有字段和非public成员,它们代表了“具体逻辑”
- 抽象类为复用而生:专门作为基类来使用,也具有解耦功能
- 封装确定的,开放不确定的,推迟到合适的子类中趋势线
- 接口是完全未实现逻辑的“类”(“纯虚类”;只有函数成员;成员全部public)
- 接口为解耦而生:“高内聚,低耦合”,方便单元测试
- 接口是一个“协约”,早已为工业生产所熟知(有分工必有协作,有协作必有协约)
- 它们都不能实例化,只能用来声明变量、引用具体类(concrete class)的实例
为做积累而生的“抽象类”
开放/关闭原则
接口与单元测试
- 接口的产生:自底向上(重构),自顶向下(设计)
- C#中接口的实现(隐式,显式,多接口)
- 语言对面向对象设计的内建支持:依赖反转,接口隔离,开/闭原则......
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication10
{
class Program
{
static void Main(string[] args)
{
int[] nums1 = new int[] { 1, 2, 3, 4, 5 };
ArrayList nums2 = new ArrayList { 1, 2, 3, 4, 5 };
Console.WriteLine(Sum(nums1));
Console.WriteLine(Avg(nums1));
Console.WriteLine(Sum(nums2));
Console.WriteLine(Avg(nums2));
}
static int Sum(IEnumerable nums)
{
int sum = 0;
foreach (var n in nums)
{
sum += (int)n;
}
return sum;
}
static double Avg(IEnumerable nums)
{
int sum = 0;
double count = 0;
foreach (var n in nums)
{
sum += (int)n;
count++;
}
return sum / count;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InterfaceExample
{
class Program
{
static void Main(string[] args)
{
var user = new PhoneUser(new NokiaPhone());
user.UsePhone();
}
}
class PhoneUser
{
private IPhone _phone;
public PhoneUser(IPhone phone)
{
_phone = phone;
}
public void UsePhone()
{
_phone.Dail();
_phone.PickUp();
_phone.Send();
_phone.Receive();
}
}
interface IPhone
{
void Dail();
void PickUp();
void Send();
void Receive();
}
class NokiaPhone : IPhone
{
public void Dail()
{
Console.WriteLine("Nokia calling ...");
}
public void PickUp()
{
Console.WriteLine("Hello! This is Tim!");
}
public void Receive()
{
Console.WriteLine("Nokia message ring ...");
}
public void Send()
{
Console.WriteLine("Hello!");
}
}
class EricssonPhone : IPhone
{
public void Dail()
{
Console.WriteLine("Ericsson calling ...");
}
public void PickUp()
{
Console.WriteLine("Hi! This is Tim!");
}
public void Receive()
{
Console.WriteLine("Ericsson message ring ...");
}
public void Send()
{
Console.WriteLine("Hi!");
}
}
}
接口隔离原则
显式接口实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication12
{
class Program
{
static void Main(string[] args)
{
IKiller killer = new WarmKiller();
killer.Kill();
var wk = (IGentleman)killer;
wk.Love();
}
}
interface IGentleman
{
void Love();
}
interface IKiller
{
void Kill();
}
class WarmKiller : IGentleman, IKiller
{
void IKiller.Kill()
{
Console.WriteLine("Kill");
}
public void Love()
{
Console.WriteLine("Love");
}
}
}
反射
- 反射与依赖注入
- 反射:以不变应万变(更松的耦合)
- 反射与接口的结合
- 反射与特性的结合
- 依赖注入:此DI非彼DI,但没有彼DI就没有此DI
partial类
- 减少类的派生
- partial类与Entity Framework
- partial类与Windows Forms, WPF, ASP.NET Core
枚举类型
- 人为限定取值范围的整数
- 整数值的对应
- 比特位式用法
结构体
- 值类型,可装/拆箱
- 可实现接口,不能派生自类/结构体
- 不能有显式无参构造器
I/0
I/O 类 | 描述 |
---|---|
BinaryReader | 从二进制流中读取原始数据 |
BinaryWriter | 以二进制格式写入原始数据 |
BufferedStream | 临时存储字节流 |
Directory | 对目录进行复制、移动、重命名、创建和删除等操作 |
DirectoryInfo | 用于对目录执行操作 |
DriveInfo | 获取驱动器的信息 |
File | 对文件进行操作 |
FileInfo | 用于对文件执行操作 |
FileStream | 用于文件中任何位置的读写 |
MemoryStream | 用于随机访问存储在内存中的数据流 |
Path | 对路径信息执行操作 |
StreamReader | 用于从字节流中读取字符 |
StreamWriter | 用于向一个流中写入字符 |
StringReader | 用于从字符串缓冲区读取数据 |
StringWriter | 用于向字符串缓冲区写入数据 |
LINQ
函数 | 作用 |
---|---|
Count() | 计数 |
Any() | 如果有符合条件的结果,则为True |
Single() | 有且只有一条满足要求的数据 |
SingleOrDefalt() | 最多只有一条满足要求的数据 |
First() | 至少有一条,返回第一条 |
FirstOrDefalt() | 返回第一条或者默认值 |
Order() | 对数据正序排序 |
OrderByDescdending() | 倒序排序 |
ThenBy() 、ThenByDescdending() | 次选排序 |
Skip(n) | 跳过n条数据 |
Take(n) | 获取n条数据 |
GroupBy() | 参数是分组条件表达式,返回值为IGrouping<TKey,TSource> 类型的泛型IEnumerable ,也就算每一组以一个IGrouping 对象的形式返回。IGrouping 是一个继承自IEnumerable 的接口,IGrouping 中Key 属性表示这一组的分组数据的值。 |
投影
把集合中的每一项转换为另外一种类型
IEnumerable<int> ages = list.Select( e=> e.Age);
IEnumerable<string> names = list.Select(e=>e.Gender:"男":"女");
var dogs = list.Select(p=>new Dog{NickName = e.Name, Age=e.Age});
集合转换
有一些地方需要数组类型或者List类型的变量,可以用ToArray()
方法和ToList()
方法分别把IEnumerable<T>
转换为数组类型和List<T>
类型。
链式调用
Where
、Select
、OrderBy
、GrupBy
、Take
、Skip
等返回值都是IEnuberable<T>
类型,所以可以链式调用。
复习
decimal
范围:-7.910^28 ~ 7.910^28 / 10^(0~28)
浮点型之间的转换只有float转换为Double一种
Ado.Net
一种数据访问技术,应用程序可以连接到数据库,并以各种方式来操作其中数据。一个COM组件库,.Net中的数据,优先选择的数据访问接口。
System.Data.dll
中,System.Data命名空间,提供不同Ado.Net类
组成:
- DataSet 非连接的核心组件。独立于任何数据源的数据访问,多种不同的数据源
- Data Provider(数据提供程序) 用于连接数据库、执行命令、检索结果
SQLServer数据提供程序 System.Data.SqlClient命名空间
OLEDB的数据提供程序 System.Data.Oledb命名空间
ODBC的数据提供程序 System.Data.Odbc命名空间
Oracle数据提供程序 System.Data.OracleClient命名空间
Connection 提供与数据源的连接 SqlConnection
Command 执行数据库命令的对象 SqlCommand
DataReader 从数据源中提供快速的、只读的数据流 SqlDataReader
DataAdapter 提供DataSet对象与数据源的桥梁Full Update
Ado.Net访问数据的步骤
连接到数据库->打开连接->创建执行命令对象(创建命令)->执行命令->关闭连接
SqlConnection类
继承于DbConnection抽象基类,不能被实例化。提供与SqlServer数据库的连接。
常用属性
State
- Closed 已关闭
- Open 打开
- Connecting 正在连接
- Executing 正在执行命令
- Fetching 正在检索数据
- Broken 连接中断