编程语言
C#编程语言基础
C#面向对象与多线程
C#数据及文件操作
JavaScript基础
JavaScript的数据类型和变量
JavaScript的运算符和表达式
JavaScript的基本流程控制
JavaScript的函数
JavaScript对象编程
JavaScript内置对象和方法
JavaScript的浏览器对象和方法
JavaScript访问HTML DOM对象
JavaScript事件驱动编程
JavaScript与CSS样式表
Ajax与PHP
ECMAScript6的新特性
Vue.js前端开发
PHP的常量与变量
PHP的数据类型与转换
PHP的运算符和优先规则
PHP程序的流程控制语句
PHP的数组操作及函数
PHP的字符串处理与函数
PHP自定义函数
PHP的常用系统函数
PHP的图像处理函数
PHP类编程
PHP的DataTime类
PHP处理XML和JSON
PHP的正则表达式
PHP文件和目录处理
PHP表单处理
PHP处理Cookie和Session
PHP文件上传和下载
PHP加密技术
PHP的Socket编程
PHP国际化编码
MySQL数据库基础
MySQL数据库函数
MySQL数据库账户管理
MySQL数据库基本操作
MySQL数据查询
MySQL存储过程和存储函数
MySQL事务处理和触发器
PHP操作MySQL数据库
数据库抽象层PDO
Smarty模板
ThinkPHP框架
Python语言基础
Python语言结构与控制
Python的函数和模块
Python的复合数据类型
Python面向对象编程
Python的文件操作
Python的异常处理
Python的绘图模块
Python的NumPy模块
Python的SciPy模块
Python的SymPy模块
Python的数据处理
Python操作数据库
Python网络编程
Python图像处理
Python机器学习
TensorFlow深度学习
Tensorflow常用函数
TensorFlow用于卷积网络
生成对抗网络GAN
一、数组、集合、索引器和泛型:
1. 数组:
程序设计中,经常需要对同一类型的数据进行统一管理和操作,为方便实现这种处理,可以使用数组数据结构。数组是一个包含相同数据类型的数据的集合,可以通过索引来访问其中的所有数据元素。C#中数组一般分为一维数组、多维数组和交错数组,此外还有隐式类型数组。数组元素可以是任意类型,包括数组类型,数值数组元素的默认值为0,引用数组元素的默认值为null。交错数组是数组的数组,它的元素是引用类型,初始化为null。
1)声明数组:
数组对象在使用前必须先声明,声明为一维数组、多维数组和交错数组的方式有所不同。
· 一维数组声明:数据类型[] 数组对象名称
· 多维数组声明:数据类型[,] 数组对象名称
· 交错数组声明:数据类型[][] 数组对象名称
其中的数据类型可以是任何C#合法的数据类型。某些情况下,数组中元素需要记录多个类型相同的数据,但数组元素包含的数据数量并不相同,此时可以使用交错数组。数组的索引从0开始,具有n个元素的数组索引从0到n-1。
2)创建数组:
数组变量需要先创建后使用,创建数组使用new关键字并指定数组的大小,也即数组最多能保存元素的个数,数组元素的个数也称数组的长度。数组可以为任意长度,但必须在创建数组时确定其大小。示例:
string[] studentsName=new string[35];
float[,] studentsGrade=new float[35,5];
对于交错数组,可以在第一次创建时指定外层数组元素的个数,每个数组元素包含元素的个数可以在后继代码中指定。示例:
int[][] jaggedArray=new int[6][];
也可以在创建数组同时设置数组元素的值。示例:
string[] studentsName=new string[]{"张三","李四","王五"};
上例中只提供了3个元素,所以数组大小为3。
也可以写为:
string[] studentsName=new string[3]{"张三","李四","王五"};
一般不指定数组大小,而由元素个数来自动确定数组大小。
3)初始化数组变量:
数组在创建后,必须先初始化才能被访问。对于数组元素的数据类型为int、float、string等基本数据类型的数组,其数组元素在数组变量创建的同时完成初始化,数组元素的值被初始化为对应类型的默认初始值。字符串类型的默认值为空,字符串长度为0。
4)访问数组元素:
对数组元素的访问是通过数组名称及元素在数组中的序号来实现,序号从0开始计数,最后一个序号为数组长度-1。
数组元素变量的值可以读也可以写。示例:
studentsName[0]="张飞";
5)遍历数组元素:
对于同一数组的数组元素依次进行处理,就是数组元素的遍历。数组遍历一般通过循环来实现,比如使用for循环:
for(int index=0;index<length;index++)
{
......
}
也可以使用while或do...while循环来实现,或是使用foreach语句,语法:
foreach(数据类型 当前变量 in 集合对象)
{
......
}
集合对象在数组中就是数组对象。foreach语句只能顺序访问数组元素,并且只能访问,不能赋值。
6)数组排序方法:
C#提供了对数组排序的方法Array.Sort和Array.Reverse,其中Array.Sort对数组从小到大排序,Array.Reverse对数组从大到小排序。示例:
int[] a={2,1,3,5,9,7};
Array.Sort(a);
7)多维数组:
二维数组由多行多列的多个元素组成,行数为第0维的长度,列数为第1维的长度。多维数组的使用与一维数组使用基本一致,但访问数组元素时,必须指定元素在每一维的序号。
8)交错数组:
某些情况下,多维数组中各维元素包含的元素个数不相同,这时可以使用交错数组。交错数组元素的维度和大小可以不同。声明并创建交错数组的示例:
int[][] jaggedArray=new int[3][];
数组实际上是由3个元素组成的一维数组,但每个元素又是一个一维数组。对这3个一维数据初始化:
jaggedArray[0]=new int[5];
jaggedArray[1]=new int[4];
jaggedArray[2]=new int[2];
每个一维数组可以使用一维数组的创建和初始化方法。交错数组还可以在声明时初始化:
int[][] jaggedArray2=new int[][]
{
new int[]{1,3,5,7,9};
new int[]{0,2,4,6};
new int[]{11,12};
};
或简化为:
int[][] jaggedArray2=
{
new int[]{1,3,5,7,9};
new int[]{0,2,4,6};
new int[]{11,12};
};
交错数组中的元素也可以为多维数组。访问交错数组中的元素时,使用数组名称及元素在数组中的对应序号实现。示例:
jaggedArray3[1][1]
9)隐式类型数组:
C#中可以创建隐式类型数组,其中数组实例的类型是从数组初始值设定项中指定的元素推断而来。开发中,隐式类型数组通常与匿名类型以及对象初始值设定和集合初始值设定一起使用。声明、创建、初始化隐式类型数组示例:
var stringArray=new[]{"hello",null,"word"};
var intHaggedArray=new[]
{
new[]{1,2,3,4};
new[]{5,6,7,8};
};
2. 集合与集合接口:
集合是用来存储和管理一组特定类型的数据对象,集合直接提供了各种数据结构及算法的实现,如队列、链表、排序等。.NET Framework提供了包括数组Array、列表List、哈希表Hashtable、字典Dictionary、队列Queue、栈Stack、双向链表LinkedList等很多常用的集合类,都位于System.Collections命名空间,在使用集合时首先要引入,集合类的功能通过实现接口Ienumerable、Icollection、Ilist、Idictionary而获得。
数组是相同数据类型的集合,只能存储数据类型相同的数据,而集合可以存储不同类型的数据,但需要对数据进行装箱、拆箱操作。创建数组时必须指定数组的长度,而使用集合中长度可以灵活增减,可以方便地进行数据的增加、删除以及插入,并且效率很高。
1)ArrayList集合:
ArrayList是一个可动态维护长度的集合,不限制所存储的类型以及个数,当不能确定数据的类型时,可以使用ArrayList集合存储数据。ArrayList始终是一维的。
ArrayList类继承了接口Ilist、Icollection、Ienumerable、Icloneable,其中封装了包括添加、删除、插入元素、查询元素索引、清空集合、排序、转换为数组等方法,提供了获取集合元素个数的属性。
⑴ 添加元素:
使用Add()方法,添加的元素会添加在集合的结尾处。方法原型:
int Add(object value);
该方法可以接受任意类型的数据,当集合的容量不够时会自动重新给集合分配内存空间,把现有的元素全部复制到新的ArrayList对象中,然后再添加新的元素到集合的末尾处,返回新的元素的索引。使用示例:
ArrayList alist=new ArrayList(10);
ArrayList alist=new ArrayList();
alist.Add(1);
alist.Add("2");
⑵ 访问元素:
ArrayList集合中,可以通过索引访问其中的元素。示例:
int a=(int)alist[0];
由于ArrayList中可以添加任意的数据类型,存储的元素都是object类型,因此在添加元素时相当于进行了一次装箱操作,把数据转换为object类型;在访问数据时就要进行一次拆箱操作,把object类型数据转换为对应的数据类型。
⑶ 删除元素:
ArrayList类提供了Remove()、RemoveAt()、Clear()方法删除集合中的元素。方法原型:
void Remove(object obj); //删除指定的元素
void RemoveAt(int index); //删除指定索引位置的元素
void Clear(); //清空集合的元素
示例:
alist.Remove(1);
alist.RemoveAt(0);
删除一个元素后,集合中后面的元素的索引会减1,集合中元素的个数会少1个。
⑷ 插入元素:
在ArrayList中插入元素使用Insert()方法,将元素插入指定位置。方法原型:
void Insert(int index, object value);
当向ArrayList中插入元素后,ArrayList会自动调整元素的索引,该元素后面的元素的索引会自动加1。
⑸ 查询元素:
在ArrayList中查询元素的索引可使用IndexOf()方法,该方法返回查询值第一次出现的位置。方法原型:
int IndexOf(object value);
⑹ 遍历:
ArrayList可以像数组一样使用循环方法遍历集合中的每个元素。
⑺ 其他方法:
排序使用Sort()方法,转为数组使用ToArray()方法,等。
2)哈希表Hashtable:
Hashtable是一种表示键值对的数据结构集合,用于处理和表现类似key/value的键值对,其中的key通常可用来快速查找,区分大小写;value用于存储对应于key的值。哈希表在存储数据时,首先会根据默认的算法自动计算哈希值,然后确定数据存储的位置。查找数据时也会通过计算数据的哈希值进行搜索,以提高查询效率。Hashtable中的key/value键值对均为Object类型,因此支持任何类型的key/value键值对。
创建哈希表的语法格式:
Hashtable 哈希表名=new Hashtable([哈希表长度],[增长因子]);
其中,哈希表长度表示初始哈希表的容量大小;增长因子表示哈希表容量不够时每次增加容量大小的倍数,是范围0~1的float类型。创建时可以省略哈希表长度和增长因子,默认增长因子为1。
创建哈希表的示例:
Hashtable ht=new Hashtable();
哈希表中也封装了很多常用方法,如添加Add、清除Clear、包含Contains、删除Remove等。
· 添加键值对:HashtableObject.Add(key,value);
· 删除键值对:HashtableObject.Remove(key);
· 通过键获得值:HashtableObject[key];
· 判断哈希表是否包含特定键:HashtableObject.Contains(key);
遍历哈希表示例代码:
static void Main(string[] args)
{
Hashtable ht=new Hashtable(10, 0.5f);
ht.Add("5", "C#");
ht.Add("3", "Java");
ht.Add("4", "C++");
ht.Add("2", "C");
foreach(object item in ht.Keys)
{
string Num=(string)item;
Console.Write(ht[num]+" ");
}
Console.WriteLine();
ht.Remove("4");
foreach(object item in ht.Values)
{
string Num=(string)item;
Console.Write(item+" ");
}
Console.Read();
}
3)栈Stack:
栈Stack是一种先入后出FILO(First In Last Out)的数据结构,或者说后进先出的数据结构。栈只能在栈顶一端添加或删除数据。创建栈的格式为:
Stack 栈名=new Stack();
在栈中添加/删除数据称为进栈/出栈,对应的方法分别为Push和Pop。操作示例:
static void Main(string[] args)
{
Stack st=new Stack();
st.Push("C#");
st.Push("Java");
st.Push("C++");
st.Push("C");
foreach(var item in st)
{
Console.Write(item);
}
st.Pop();
foreach(var item in st)
{
Console.Write(item);
}
Console.Read();
}
4)队列Queue:
队列Queue是一种先进先出FIFO(First In First Out)的数据结构。队列只能在队的一端添加数据或者删除数据。创建队列的一般格式:
Queue 队列名=new Queue();
向队中添加/删除数据称为进队/出队,对应的方法分别是Enqueue和Dequeue。
static void Main(string[] args)
{
Queue q=new Queue();
q.Enqueue("C#");
q.Enqueue("Java");
q.Enqueue("C++");
q.Enqueue("C");
foreach(var item in q)
{
Console.WriteLine(item);
}
q.Dequeue();
foreach(var item in st)
{
Console.WriteLine(item);
}
Console.Read();
}
队列中最先输出的是最先添加的元素,最后输出的是最后添加的元素。删除元素也是删除最先添加的元素,即队头的元素。
3. 泛型:
C# 2.0中引入了泛型Generic,泛型是类型的模板,它提供了一种以上类型代码的方法。泛型允许使用类型参数化的代码,即可以使用不同的类型进行实例化。C#提供了5种泛型,包括类、接口、结构、委托和方法。使用泛型,可以让代码应用起来更加灵活,提高了代码的可复用性。
1)泛型集合:
泛型最常见的应用是集合类,用的最多的泛型集合是List<T>、Dictionary<T, M>等,其中T和M表示集合中元素的类型。任何类型的对象要存储在集合类中都必须强制转化为Object类型,从集合类中获取元素时又要转化为原先的类型。为了提供更强的编译时类型检查,减少数据类型之间的显式转换,以及装箱操作和运行时类型检查,引入泛型。
⑴ 泛型List集合List<T>:
List<T>是动态数组ArrayList的泛型等效类,是指定类型的列表。List<T>不需要声明长度,可动态自动对内存空间进行管理。List<T>必须指定列表中元素的类型,在添加元素时会对数据类型进行严格检查,并且列表在访问集合元素时不需要做装箱或拆箱操作,可以直接访问,从而提高了效率。
创建列表List<T>的格式为:
List<元素类型> 对象名=new List<元素类型>;
其中,前后两个元素类型必须保持一致,在使用列表时需要引入System.Collections.Generic命名空间。
示例:
List<int> primes=new List<int>();
primes.Add[(1);
primes.Add[(3);
int pSum=primes[0]+primes[1];
示例2:
static void Main(string[] args)
{
List<string> subject=new List<string>();
subject.Add("C#");
subject.Add("C++");
subject.Add("Java");
foreach(string item in subject)
{
Console.WriteLine(item);
}
Console.Read();
}
上述代码中创建了一个string类型的列表,列表中只能添加string类型的数据。List
⑵ Dictionary<T, M>:
字典Dictionary是键值的集合,本质是哈希表。在使用字典时需要指定键值的类型,因此访问数据时也不需要装箱或拆箱操作。
创建字典的格式:
Dictionary<键类型, 值类型> 对象名=new Dictionary<键类型, 值类型>();
创建一个学生信息的字典示例:
static void Main(string[] args)
{
Dictionary<int, string> student=new Dictionary<int, string>();
student.Add(100,"jack");
student.Add(90,"rose");
student.Add(88,"marry");
foreach(string item in student.Values)
{
Console.WriteLine(item);
}
Console.Read();
}
上述代码中创建了一个键为int类型、值为string类型的学生字典,然后添加几条数据。
⑶ 泛型Stack:
Stack称为堆栈,是一种线性数据结构,只能在一端进行数据输入和输出操作,遵循先进后出原则,有一个固定的栈底和浮动的栈顶。向栈中输入数据的操作称为入栈,被压入的数据保存在栈顶,同时栈指针上浮一个;从栈中输出数据的操作称为出栈,只有栈顶元素才能出栈,如果栈顶指针指向栈底,说明当前栈是空的。创建栈示例:
Stack<string> stk=new Stack<string>();
Stack类是用来实现栈的工具类,实现的主要方法:
· 将指定对象压入栈顶:Push(Object obj)
· 移出并返回栈顶的对象:Pop()
· 返回位于栈顶的对象,但不将此对象移出:Peek()
⑷ 泛型Queue:
队列Queue也是一种线性数据结构,在按顺序存储消息方面非常有用。队列在一端输入数据,在另一端输出数据。队列中数据的插入和删除都只能在队列的头尾处进行,遵循先进先出的原则。每个队列都有容量,如果存储的元素数达到它的容量,这个容量会自动增加以满足需要,队列提供一个增长系数,表示队列满时容量的增加值。用户也可以在Queue类的构造函数中设定增长系数,或者使用默认值2.0。创建队列示例:
Queue<string> q=new Queue<string>();
Queue类是用来实现队列的工具类,能实现队列操作的主要方法:
· 移出并返回位于队列开始处的对象:Dequeue()
· 将对象添加到队列队尾:Enqueue(object obj)
· 返回位于队列开始处的对象,但不将该对象移出:Peek()
2)泛型类、泛型方法和泛型接口:
也可以自定义自己的泛型类、泛型方法和泛型接口。
⑴ 泛型类:
声明泛型类与声明普通类的方法差不多,但是需要在类名后放一组尖括号,并在尖括号中使用类型参数列表表示希望使用的类型;而在泛型类中使用类型参数表示应该被替换的类型。语法格式:
[访问修饰符] class 泛型类名<类型参数列表>[:基类或接口][类型参数约束]
其中,访问修饰符包括public、private、protected等;类型参数列表可以是一个类型,也可以是多个类型。多个类型之间用逗号分隔;泛型类可以继承自基泛型类或者接口;类型参数约束用来限定泛型类中参数的类型。示例:
public class student<T, M>
{
public T t {get; set;}
public M m {get; set;}
public student(T x, M y)
{
t=x;
m=y;
}
}
使用泛型可以灵活地创建类,比如用一个学生的年龄和姓名即可创建一个int和string类型的学生对象,代码:
student<int, string> s1=new student<int, string>(22,"Jack");
用一个学生的学号和姓名即可创建两个string类型的学生对象,代码:
student<string, string> s2=new student<string, string>("87","Jack");
在定义泛型时,为了让泛型更有用,有时需要提供额外的信息让编译器知道这个泛型类只能接收哪些类型,这些额外信息称为约束。约束使用where子句列出,语法:
where 类型参数: 约束列表
其中,where在类型参数列表的右尖括号之后列出,约束列表可以是一个,也可以是多个。示例:
class Program
{
static void Main(string[] args)
{
client c1=new client();
customer cu=new customer();
person<client, customer> s1=new person<client, customer>(c1,cu);
}
}
public class person<T, M>where T: client where M: customer
{
public T t {get; set;}
public M m {get; set;}
public persont(T x, M y)
{
t=x;
m=y;
}
}
public class client
{
public string name {get; set;}
}
public class customer
{
public string name {get; set;}
}
上述代码中,定义了client类、customer类和person类,其中person是泛型类,泛型参数T、M分别进行了约束,T只能是client类或者其子类,M只能说customer类或者其子类。
C#中一共有5种约束类型。对于有多种约束的泛型类,where子句中的约束必须有特定的顺序。可以有多个interfaceName约束,如果存在构造函数约束则必须放在最后。
⑵ 泛型方法:
约束类型
描述
类名
只有这个类型的类或其子类可以作为类型实参
class
任何引用类型都可以作为类型实参
struct
任何值类型都可以作为类型实参
interfaceName
只有这个接口或实现这个接口的类型可以作为类型实参
new()
任何带有无参数公共构造函数的类型都可以作为类型实参
泛型方法与泛型类一样有泛型参数列表和可选约束。语法格式:
[访问修饰符] 返回值类型 方法名<类型参数列表>(形式参数列表)[类型参数约束]
示例:
static void Main(string[] args)
{
int x=3;
int y=5;
string a="123";
string b="456";
Console.WriteLine("交换之前:x={0} y={1}", x, y);
Console.WriteLine("交换之前:a={0} b={1}", a, b);
Swap<int>(ref x, ref y);
Swap<string>(ref a, ref b);
Console.WriteLine("交换之后:x={0} y={1}", x, y);
Console.WriteLine("交换之后:a={0} b={1}", a, b);
Console.Read();
}
static void Swap<T>(ref T x, ref T y)
{
T temp;
temp=x;
x=y;
y=temp;
}
⑶ 泛型接口:
泛型接口允许编写参数和接口成员返回类型是泛型类型参数的接口。泛型接口与普通接口的声明相似,但是需要在尖括号中有类型参数。定义泛型接口的语法格式:
interface 泛型接口名<类型参数列表>
示例:
class Program
{
static void Main(string[] args)
{
myclass<int> m=new myclass<int>();
int value1=m.ReturnValue(1);
myclass<string> ms=new myclass<string>();
string value2=ms.ReturnValue("123");
Console.WriteLine("intValue={0} stringValue={1}", value1, value2);
Console.Read();
}
}
interface IMyinterface<T>
{
T ReturnValue(T Tvalue);
}
public class myclass<T>: IMyinterface<T>
{
public T ReturnValue(T Tvalue){return Tvalue;}
}
当需要一个类实现两个不同类型参数的泛型接口时,就需要实例化两个接口。在实现泛型接口时,必须保证类型实参组合不会在类型中产生两个重复的接口。示例:
class Program
{
static void Main(string[] args)
{
myclass m=new myclass();
int intValue=m.ReturnValue(1);
string strValue=m.ReturnValue("123");
Console.WriteLine("intValue={0} stringValue={1}", intValue, strValue);
Console.Read();
}
}
interface IMyinterface<T>
{
T ReturnValue(T Tvalue);
}
public class myclass: IMyinterface<int>, IMyinterface<string>
{
public int ReturnValue(int intValue){return intValue;}
public string ReturnValue(string stringValue){return stringValue;}
}
二、LINQ技术:
LINQ是语言集成查询(Language-Integrated Query)的缩写,能够将查询功能直接引入.NET Framework所支持的编程语言中。LINQ引入了标准的、易于学习的查询和更新数据模式,可以对其技术进行扩展,以支持几乎任何类型的数据存储。
LINQ是.NET框架的扩展,允许用户使用SQL查询数据库的方式查询数据集合。使用LINQ可以从数据库、程序对象集合以及XML文档中查询数据。
LINQ可以和各种类型的数据源一起工作,在每种数据源的背后一定有根据该数据源类型实现的LINQ查询的代码模块,这些代码模块称为LINQ的提供程序Provider。
1. 匿名类型:
匿名类型Anonymous Type经常用于LINQ查询结果中。创建匿名类型的对象语法:
new (FieldProp=InitExpr, FieldProp=InitExpr, ...)
匿名类型只能和局部变量配合使用,不能用于类成员。由于匿名类型没有名字,所以必须使用var关键字作为变量类型。不能设置匿名类型对象的属性,编译器为匿名类型创建的属性是只读的。当编译器遇到匿名类型的对象初始化语句时,创建一个有名字的新类类型。对于每个成员初始化语句,它推断其类型并创建一个只读属性来访问它的值,属性和成员初始化语句有相同的名字。匿名类型被构造后,编译器即创建这个类型的对象。创建匿名类型示例:
namespace example
{
class Program
{
static void Main(string[] args)
{
var student=new {Name="Jerry", Age=19, Major="History"};
Console.WriteLine("{0}, Age {1}, Major: {2}", student.Name, student.Age, student.Major);
Console.ReadKey();
}
}
}
除了对象初始化语句的赋值形式外,匿名类型的对象初始化语句还有简单标识符和成员表达式两种形式,这两种形式称为投影初始化语句。示例:
var student=new { Age=19, Other.Name, Major};
注意,投影初始化语句必须定义在匿名类型声明之前。示例:
namespace example
{
class Other
{
static public string Name="Jerry";
}
class Program
{
static void Main(string[] args)
{
string Major="History";
var student=new { Age=19, Other.Name, Major};
Console.WriteLine("{0}, Age {1}, Major: {2}", student.Name, student.Age, student.Major);
Console.Read();
}
}
}
如果编译器遇到了另外一个具有相同参数名、相同的推断类型和相同顺序的匿名类型,它会重用这个类型并直接创建新的实例,不会创建新的匿名类型。
2. 方法语法和查询语法:
在编写LINQ程序时,可以使用方法语法和查询语法两种形式。方法语法使用标准的方法调用,这些方法是一组标准查询运算符的方法;而查询语法看上去与SQL语句相似。一个查询中可以采用两种形式的结合。
方法语法是命令形式的,它指明了查询方法调用的顺序;查询语法是声明形式的,即查询描述的是想返回的内容,但并没有指明如何执行这个查询。编译器会将使用查询语法表示的查询翻译为方法调用的形式,这两种形式在运行时没有性能上的差异。
微软公司推荐使用查询语法,因为它更易读,能更清晰地表明查询意图,因此不容易出错。但一些运算符必须使用方法语法来书写。示例:
namespace example
{
class Program
{
static void Main(string[] args)
{
int numbers={8,3,2,9,55,27,36};
var numsQuery=from n in numbers where n<20 select n; //查询语法
var numsMethod=numbers.where(x=>x<20); //方法语法
int numsCount=(from n in numbers where n<20 select n).Count(); //结合两种
foreach(var x in numsQuery)
{
Console.WriteLine("{0},", x);
}
Console.WriteLine();
foreach(var x in numsMethod)
{
Console.WriteLine("{0},", x);
}
Console.WriteLine();
Console.WriteLine(numsCount);
Console.Read();
}
}
}
3. 查询变量:
LINQ可以返回两种类型的结果,可以是枚举值,它满足查询参数的项列表;也可以是称为标量的单一值,它是满足查询条件的结果的某种摘要形式。示例:
int[] numbers={7,3,15,27};
IEnumberable
int numsCount=(from n in numbers where n<20 select n).Count(); //返回一个整数
如果查询表达式返回枚举值,查询直到处理枚举值时才会执行,如果枚举值被处理多次,查询就会执行多次。如果在遍历后,查询执行之前的数据有变动,则查询会使用新的数据。
如果查询表达式返回标量,查询立即执行,并把结果保存在查询变量中。
4. 查询表达式的结构:
查询表达式是根据LINQ语法书写的查询,它与其他表达式一样可以在C#语句中直接使用。查询表达式由一组用类似于SQL的声明性语法编写的子句组成,每个子句又包含一个或多个C#表达式,而这些表达式本身有可能是查询表达式或包含查询表达式。
查询表达式必须以from子句开头且必须以select或group子句结尾。在第一个from子句和最后一个select或group子句之间,查询表达式可以包含一个或多个下列的可选子句:where、orderby、join、let甚至附加的from子句。另外,还可以使用关键字into使join或group子句的结果能够充当同一查询表达式中附加查询子句的数据源。LINQ表达式子句列表:
子句 | 说明 |
---|---|
from | 指定数据源和范围变量 |
select | 指定当前执行查询时返回的序列中的元素将具有的类型和形式 |
group | 按照指定的键值对查询结果进行分组 |
where | 根据一个或多个由逻辑与和逻辑或运算符分隔的布尔表达式筛选源元素 |
orderby | 基于元素类型的默认比较器按升序或降序对查询结果进行排序 |
join | 基于两个指定匹配条件之间的值的比较来连接两个数据源 |
let | 引入一个用于存储查询表达式中的子表达式结果的范围变量 |
into | 提供一个标识符,它可以充当对join、group或select子句的结果的引用 |
在LINQ中需要指定数据源,C#中必须先声明变量才能使用。LINQ中的from子句的目的是引入数据源和范围变量。示例:
var queryStudent=from stu in student select stu;
执行查询时,范围变量将用作对数据源中的每个后续元素的引用。
⑵ 筛选:
使用where子句,最常用的查询操作是应用布尔表达式的筛选器,以使查询只返回那些表达式结果为true的元素。示例:
var queryStudent=from stu in student where stu.sex=="男" select stu;
也可以使用C#中的逻辑与、逻辑或运算符来根据需要在where子句中应用。
⑶ 排序:
使用orderby子句可以很方便地将返回数据进行排序,可以选择升序或降序。示例:
var queryStudent=from stu in student orderby stu.age descending select stu;
如果要对查询结果升序排列,使用orderby... ascending。
⑷ 分组:
group子句把select对象根据一些标准进行分组。如果项包含在查询结果中,就可以根据某个字段的值进行分组,作为分组依据的属性称为键key。示例:
var queryStudent=from stu in student group s by s.Major;
⑸ 连接:
join子句可结合两个或多个集合中的数据,创建一个临时的对象集合,每个对象包含原始集合对象中的所有字段。语法:
join Identifier in Collection2 on Fields1 equals Field1
示例:
var query=from s in students join c in studentsInCourses on s.StID equals c.StID
LINQ中的join接受两个集合,然后创建一个新集合,每个元素包含两个原始集合中的原始成员。
三、ADO.NET编程:
ADO.NET是微软新一代的.NET数据库访问架构,其中ADO是ActiveX Data Object的缩写。ADO.NET是数据库和数据源之间沟通的桥梁,主要提供一个面向对象的数据访问架构,用来开发数据库应用程序。
1. ADO.NET对象模型:
ADO.NET中包含多个对象模型,如Connection、Command、DataReader、DataAdapter、Parameter、DataSet、DataTable等。
1)Connection对象:
Connection对象用于连接到数据库并管理数据库中的事务,该对象提供一些方法,运行开发人员与数据源建立连接或者断开连接。微软公司提供了4种数据连接对象,包括SqlConnect(对应SQL Server)、OleDbConnection(对应OLE DB)、OdbcConnection(对应ODBC DB)、OracleConnect(对应Oracle)。
Connection对象常用属性:
属性 | 说明 |
---|---|
ConnectionString | 获取或设置用于打开数据库的字符串 |
ConnectionTimeout | 获取在尝试建立连接时终止尝试并生成错误之前所等待的时间 |
Database | 获取当前数据库或连接打开后要使用的数据库名称 |
DataSource | 获取要连接的数据库服务器名称 |
State | 指示数据库的连接状态 |
方法 | 说明 |
---|---|
BeginTransaction() | 开始数据库事务 |
ChangeDatabase() | 更改当前数据库 |
ChangePassword() | 将连接字符串中指示的用户的数据库密码更改为提供的新密码 |
ClearAllPools() | 清空连接池 |
Close() | 关闭与数据库的连接 |
CreateCommand() | 创建并返回一个与Connection关联的Command对象 |
Dispose() | 释放由Connection使用的所有资源 |
Open() | 使用ConnectionString属性所指定的属性设置打开数据库连接 |
Command对象用来对数据源执行查询、添加、删除、修改等各种操作,操作实现的方式可以使用SQL语句,也可以使用存储过程。根据.NET Framework数据提供程序的不同,Command对象可以分成4种,分别是SqlCommand、OleCommand、OdbcCommand、OracleCommand。实际编程中要根据访问的数据源的不同选择相应的Command对象。
Command对象常用属性:
属性 | 说明 |
---|---|
CommandType | 获取或设置Command对象要执行命令的类型 |
CommandText | 获取或设置要对数据源执行的SQL语句或存储过程名或表名 |
CommandTimeout | 获取或设置在终止对执行命令的尝试并生成错误之前的等待时间 |
Connection | 获取或设置此Command对象使用的Connection对象的名称 |
Parameters | 获取Command对象需要使用的参数集合 |
Transaction | 获取或设置将在其中执行Command的SqlTransaction |
方法 | 说明 |
---|---|
ExecuteNonQuery() | 用于执行非SELECT命令,如INSERT、DELETE、UPDATE命令,返回命令所影响的数据的行数,也可以用来执行一些数据定义命令,如新建、更新、删除数据库对象(如表、索引等) |
ExecuteScalar() | 用于执行SELECT查询命令,返回数据中第一行第一列的值,通常用来执行用到COUNT()或SUM()方法的SELECT语句 |
ExecuteReader() | 执行SELECT命令,返回一个DataReader对象,这是只读的数据集 |
DataReader对象是一个简单的数据集,用于从数据源中读取只读的数据集,常用于检索大量数据。根据.NET Framework数据提供程序的不同,DataReader可以分成SqlDataReader、OleDbDataReader等几类。DataReader每次只能在内存中保留一行,因此开销非常小。
使用DataReader对象读取数据时,必须一致保持与数据库的连接,称为连线模式,读取完数据后才能断开连接。DataReader是一个轻量级数据对象,如果只需要将数据读出并显示是最合适的工具,读取速度比DataSet对象快,占用资源也少。DataReader对象常用属性:
属性 | 说明 |
---|---|
Connection | 获取与DataReader关联的Connection对象 |
HasRows | 判断数据库中是否有数据 |
FieldCount | 获取当前行的列数 |
IsClosed | 检索一个布尔值,指示是否已关闭指定的DataReader实例 |
Item | 在给定列序号或列名称情况下,获取指定列的以本机格式表示的值 |
RecordsAffected | 获取执行SQL语句所更改、添加或删除的行数 |
方法 | 说明 |
---|---|
IsDBNull() | 获取一个值,用于指示列中是否包含不存在的或缺少的值 |
Read() | 使DataReader对象前进到下一条记录 |
NextResult() | 当读取批处理Transact-SQL语句的结果时,使数据读取器前进到下一个结果 |
Close() | 关闭DataReader对象 |
Get() | 用来读取数据集当前行的某一列数据 |
为了避免应用程序出现SQL注入式攻击,ASP.NET提供了一个SqlParameter对象,提供类型检查和验证,使命令对象可使用参数来将值传递给SQL语句或存储过程。参数输入被视为文本值,而不是可执行代码,可帮助抵御SQL注入式攻击,从而保证应用程序的安全。同时,该对象可帮助数据库服务器将传入命令与适当的缓存查询计划进行准确地匹配,从而提高查询执行的效率。Parameter可以分成SqlParameter、OracleParameter等几类。
Parameter对象常用属性:
属性 | 说明 |
---|---|
DbType | 获取或设置参数的数据库类型 |
Direction | 获取或设置一个值,指示参数是只可输入的参数,或是只可输出的参数。或是双向参数,或是存储过程返回值参数。在添加参数时必须为输入参数以外的参数提供一个ParameterDirection属性,该属性是一个枚举类型,包含: · Input:该参数为输入参数,默认设置值 · InputOutput:该参数可执行输入和输出操作 · Output:该参数为输出参数 · ReturnValue:该参数表示从某操作返回的值 |
IsNullable | 获取或设置一个值,指示参数是否接受null值 |
ParameterName | 获取或设置Parameter的名称 |
Size | 获取或设置列中的数据的最大值,以字节为单位 |
TypeName | 获取或设置表值参数的类型名称 |
Value | 获取或设置参数的值 |
方法 | 说明 |
---|---|
Parameter() | 初始化Parameter类的新实例 |
Parameter(string, Object) | 初始化Parameter类的新实例,使用参数名称和Parameter类新实例的值 |
Parameter(string, SqlDbType) | 使用提供的参数名称和数据类型初始化Parameter类的新实例 |
DataAdapter对象用来充当DataSet对象与实际数据源之间的桥梁,是专门为DataSet服务的。DataAdapter对象的工作一般有两种,一是通过Command对象执行SQL语句从数据源中检索数据,将获取的结果填充到DataSet对象的表中;二是把用户对DataSet对象作出的更改写入数据源中。
DataAdapter对象常用属性:
属性 | 说明 |
---|---|
SelectCommand | 获取或设置用于在数据源中选择记录的命令 |
InsertCommand | 获取或设置用于将新记录插入数据源中的命令 |
UpdateCommand | 获取或设置用于更新数据源中记录的命令 |
DeleteCommand | 获取或设置用于从数据源中删除记录的命令 |
方法 | 说明 |
---|---|
AddToBatch() | 向当前批处理中添加Command对象 |
ExecuteBatch() | 执行当前批处理 |
Fill() | 从数据源中提取数据以填充数据集 |
FillSchema() | 从数据源中提取数据架构以填充数据集 |
Update() | 更新数据源 |
DataSet是ADO.NET的核心成员之一,它是支持ADO.NET断开式、分布式数据方案的核心对象,也是实现基于非连接的数据查询的核心组件。DataSet对象是创建在内存中的集合对象,可以包含任意数量的数据表,以及所有表的约束、索引和关系,相对于内存中的一个小型关系数据库。一个DataSet对象包括一组DataTable对象和DataRelation对象,其中每个DataTable对象由DataRow、DataColunm、Constraint对象组成。
DataSet常用属性:
属性 | 说明 |
---|---|
Relations | 获取用于将表连接起来并允许从父表浏览子表的关系的集合 |
Tables | 获取包含在DataSet中的表的集合 |
方法 | 说明 |
---|---|
AcceptChanges() | 提交自加载此DataSet或上次调用AcceptChanges以来对其进行的所有更改 |
Clear() | 通过移出所有表中的所有行清除所有数据的DataSet |
Clone() | 复制DataSet结构,包括所有DataTable架构、关系和约束,不复制任何数据 |
Copy() | 复制该DataSet的结构和数据 |
GetXml() | 返回存储在DataSet中的数据的XML表示形式 |
Merge() | 将指定的DataSet及其架构合并到当前的DataSet中 |
ReadXml() | 使用指定的文件或流将XML架构和数据读入DataSet |
WriteXml() | 将DataSet的当前数据写入指定的文件或者流中 |
2. 数据访问类SqlHelper:
SqlHelper是一个基于.NET Framework的数据库操作组件,该组件将增、删、改、查等数据库操作进行封装,只需要向方法传入一些参数即可操作数据库,操作简单方便,提供了代码的可重用性。SqlHelper有两个版本,一个是微软在EnterpriseLibrary中提供,另一个是开源组织在DBHelper中提供。
对SQL Server数据库最常见的操作有4种:非连接式查询获取DataTable,连接式查询获取DataReader,查询结果只有1行1列获取单一数据,增、删、改操作返回受影响的行数。示例代码:
using System.Data;
using System.Data.SqlClient;
public class SqlHelper
{
private static readonly string ConnectionString="Data Source=
PC-201902241945;Initial Catalog=BMSDB:User ID=sa;Password=123456;";
public static DataTable ExecuteDataTable(string strSql, CommandType cmdType, params SqlParameter[] pms)
{
DataTable dt=new DataTable();
using (SqlDataAdapter adapter=new SqlDataAdapter(strSql, ConnectionString))
{
adapter.SelectCommand.CommandType=cmdType;
if(pms!=null)
{
adapter.SelectCommand.Parameters.AddRange(pms);
}
adapter.Fill(dt);
return dt;
}
}
public static SqlDataReader ExecuteReader(string strSql, CommandType cmdType, params SqlParameter[] pms)
{
SqlConnection con=new SqlConnection(ConnectionString);
using (SqlCommand cmd=new SqlCommand(strSql, con))
{
cmd.CommandType=cmdType;
if(pms!=null)
{
cmd.Parameters.AddRange(pms);
}
try
{
con.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
catch(Exception ex)
{
con.Close();
con.Dispose();
throw;
}
}
}
public static object ExecuteScalar(string strSql, CommandType cmdType, params SqlParameter[] pms)
{
using (SqlConnection con=new SqlConnection(ConnectionString))
{
using (SqlCommand cmd=new SqlCommand(strSql, con))
{
cmd.CommandType=cmdType;
if(pms!=null)
{
cmd.Parameters.AddRange(pms);
}
con.Open();
return cmd.ExecuteScalar();
}
}
}
public static object ExecuteNonQuery(string strSql, CommandType cmdType, params SqlParameter[] pms)
{
using (SqlConnection con=new SqlConnection(ConnectionString))
{
using (SqlCommand cmd=new SqlCommand(strSql, con))
{
cmd.CommandType=cmdType;
if(pms!=null)
{
cmd.Parameters.AddRange(pms);
}
con.Open();
return cmd.ExecuteNonQuery();
}
}
}
}
四、文件操作:
应用程序中对数据的操作的数据源大多数来自于文件,文件一般存储在磁盘中。对于文件的读/写、修改等操作是应用程序处理文件的基本操作,.NET Framework提供了文件操作功能。
1. 文件的输入/输出:
在.NET Framework框架中,文件是存储在介质上的静态数据,它具有文件名、扩展名以及文件的存储路径。流Stream是当对数据进行读写时所形成的一种状态,流不仅是打开的磁盘文件,也可以是网络上传输的数据,或控制台输入/输出状态的数据。流包括读取read、写入write、定位seek三种基本操作,read表示把数据从流输出到某种数据结构,如输出到数组中;write表示把数据从某种数据结构输入到流中,如从数组到流;seek表示在流中查询当前位置。
1)与流相关的类:
与流相关的类位于System.IO命名空间中,常用的类有Stream、TextReader、TextWriter类及其派生类,还有FileStream、MemoryStream、BufferStream类。
⑴ Stream类:
Stream类是所有流的抽象基类,主要属性有CanRead、CanWrite、Length、Position等,主要方法有Read、Write、Flush、Close等。
⑵ TextReader类和TextWriter类:
TextReader类是一个可读取连续字符系列的读取器,是StreamReader类和StringReader类的抽象基类;TextWriter类是一个可写入连续字符系列的书写器,是StreamWriter类和StringWriter类的抽象基类。StreamReader类和StreamWriter类使用Encoding编码,StreamReader类从流或文本文件中读取字符,StreamWriter类向流或者文本文件写入字符。StringReader类从字符串中读取字符,StringWriter类向字符串中写入字符。
⑶ FileStream、MemoryStream、BufferStream类:
文件流FileStream类以流的形式读/写、打开或关闭文件;内存流MemoryStream类用来在内存中创建流,保存临时数据,可直接对它进行读/写、查找操作,提高文件访问的效率;缓存流BufferStream类先把流添加到缓冲区,再对数据进行读/写操作,减少访问数据的次数。
2)读/写文本文件:
文本文件保存的是字符的编码,.NET中提供了多种编码,包括ASCII、UTF8、UTF7、Unicode、UTF32等。
读/写文本文件主要使用文本读取器TextReader和文本写入器TextWriter,也可以使用派生类流读取器StreamReader和流写入器StreamWriter。读写文本文件的常用方法有Read()\、ReadLine()、ReadToEnd()、Write()、WriteLine()、Flush()、Close()。
示例:
StreamReader ar=new StreamReader(@"C:/Users/example.txt",Encoding.Default);
txt1.Text=sr.ReadToEnd();
sr.Close();
//
StreamWriter sw=new StreamWriter(@"C:/Users/example.txt", false, Encoding.Default);
sw.WriteLine(txt.Text);
sw.WriteLine(DateTime.Now.ToString());
sw.Close();
新版本的C#支持一种更简单的文件读写方式,打开文件、写入文本、关闭文件用一个语句就能实现,示例:
File.AppendAllText(logfilename, cmdStr);
上述代码实现打开logfilename文件,在文件末尾追加写入cmdStr字符串,然后关闭文件,使用非常简便。
3)读/写二进制文件:
二进制文件存储的是字节序列,可以存储图像、声音、文本或者编译后的程序代码。在.NET中,读/写二进制文件主要使用BinaryReader类和BinaryWriter类,这两个类都位于System.IO命名空间。BinaryReader类的主要方法有Read()、Close();BinaryWriter类中的主要方法有Write()。
在使用读/写二进制文件的类时,需要先构造一个FileStream类或MemoryStream类或BufferStream类的对象,然后再使用BinaryReader类和BinaryWriter类。示例:
FileStream fl=new FileStream(@"C:/Users/example.txt", FileMode.Append, FileAccess.Write);
BinaryWriter bw=new BinaryWriter(fl);
bw.Write(txtName.Text);
bw.Write(double.Parse(txtPrice.Text));
bw.Write(int.Parse(txtCount.Text));
bw.Close();
fl.Close();
//
FileStream fl=new FileStream(@"C:/Users/example.txt", FileMode.OpenOrCreate, FileAccess.Read);
BinaryReader br=new BinaryReader(fl);
string name=br.ReadString();
double price=br.ReadDouble();
int count=br.ReadInt32();
string rt=string.Format("{0}\t{1}\t{2}", name, price, count);
lstdata.Items.Add(rt);
br.Close();
fl.Close();
4)对象的序列化:
当要读取写入的数据具有实体关系时,采用二进制读/写文件需要每次单个写入比较麻烦,这时使用对象序列化的方法读/写文件就比较方便。把数据封装为一个对象,采用.NET的对象序列化方法,将对象转换为字节流写入数据,再采用.NET的对象反序列化方法将字节流转换为对象从而读出数据。
.NET Framework提供了BinaryFormatter类和SoapFormatter类支持对象序列化与反序列化的功能,Binary
实现对象序列化的步骤:首先将需要序列化的实体使用Serialization进行标记,然后调用BinaryFormatter类或者SoapFormatter类的Serialize()方法实现对象的序列化或者调用Deserialize()方法进行对象的反序列化。示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace wffileOperate
{
[Serializable] //序列化标记一个类
public class Fruit
{
public string name {get; set;}
public double price {get; set;}
public int count {get; set;}
public Fruit(string name, double price, int count)
{
this.name=name;
this.price=price;
this.count=count;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
using System.Wondows.Forms;
namespace wffileOperate
{
[Serializable] //序列化标记一个类
public partial class Form2: Form
{
public IList<Fruit> flist {get; set;} //用来存储数据后再序列化写入磁盘
public Form2()
{
InitializeComponent();
flist=new List
}
public void btnSave_Click(object sender, EventArgs e)
{
//对象序列化写文件
FileStream fl=new FileStream(@"C:/Users/fruit.txt", FileMode.Append, FileAccess.Write);
BinaryFormatter bf=new BinaryFormatter();
bf.Serialize(fl, flist); //对象集合列表flist序列化写入数据
fl.Close();
}
public void btnShow_Click(object sender, EventArgs e)
{
//对象反序列化读文件
FileStream fl=new FileStream(@"C:/Users/fruit.txt", FileMode.OpenOrCreate, FileAccess.Read);
BinaryFormatter bf=new BinaryFormatter();
IList<Fruit> fruits=(List<Fruit>) bf.Deserialize();
lstdata.Items.Add("名称\t价格\t数量");
foreach(var item in fruits)
{
string rt=string.Format("{0}\t{1}\t{2}", item.name, item.price, item.count);
lstdata.Items.Add(rt);
}
fl.Close();
}
public void btnAdd_Click(object sender, EventArgs e)
{
string name=txtName.Text;
double price=double.Parse(txtPrice.Text);
int count=int.Parse(txtCount.Text);
Fruit f=new Fruit(name, price, count);
flist.Add(f);
}
}
}
2. 文件操作控件:
对于文件操作,除了基本的读/写以外,用户更多的时候需要使用可视化的窗口交互操作,如对话框、消息框等。.NET提供了SaveFileDialog、OpenFileDialog、FolderBrowseDialog、ColorDialog、FontDialog等控件实现交互。
1)SaveFileDialog控件和OpenFileDialog控件:
SaveFileDialog控件和OpenFileDialog控件都继承自FileDialog类,分别表示文件“另存为”对话框和“打开”文件对话框。FileDialog类常用的属性见下表:
属性 | 类型 | 说明 |
---|---|---|
CheckFileExists | bool | 当用户指定的文件不存在时是否警告 |
CheckPathExists | bool | 检验用户选择的文件路径是否存在 |
DefaultExt | string | 设置或获取文件默认的扩展名 |
FileName | string | 获取或设置对话框中选择的文件名 |
FileNames | string[] | 获取或设置对话框中选择的所有文件名 |
Filter | string | 设置或获取筛选的文件类型 |
Title | string | 设置或获取对话框的标题 |
事件及方法 | 返回值类型 | 说明 |
---|---|---|
FileOK() | CancelEventHandler | 用户单击对话框上的“打开”或“保存”按钮时触发 |
Reset() | void | 所有属性值重置为默认值 |
ShowDialog() | DialogResult | 显示对话框 |
private void btnOpen_Click(object sender, EventArgs e)
{
if(openFileDialog1.ShowDialog()==DialogResult.OK)
{
txtPath.Text=openFileDialog1.FileName;
}
}
private void btnSave_Click(object sender, EventArgs e)
{
saveFileDialog1.ShowDialog();
}
private void saveFileDialog1_FileOK(object sender, CancelEventArgs e)
{
Stream fl=saveFileDialog1.OpenFile();
BinaryFormatter bf=new BinaryFormatter();
bf.Serialize(fl, flist);
fl.Close();
MessageBox.Show("数据保存成功");
}
private void openFileDialog1_FileOK(object sender, CancelEventArgs e)
{
FileStream fl=new FileStream(openFileDialog1.FileName, FileMode.OpenOrCreate, FileAccess.Read);
BinaryFormatter bf=new BinaryFormatter();
List<Fruit> fruits=(List<Fruit>) bf.Deserialize(fl);
lstdata.Items.Add("名称\t价格\t数量");
foreach(var item in fruits)
{
string rt=string.Format("{0}\t{1}\t{2}", item.name, item.price, item.count);
lstdata.Items.Add(rt);
}
fl.Close();
}
2)FolderBrowseDialog、ColorDialog、FontDialog控件:
FolderBrowseDialog控件让用户浏览文件夹或创建新文件夹,提供的主要属性有RootFolder、SelectPath,主要方法有ShowDialog()。ColorDialog控件给用户提供选择颜色的方式,主要属性为Color对象。FontDialog控件供用户选择字体的大小和字号,主要属性有Font对象和Color对象。示例:
private void openFile_Click(object sender, EventArgs e)
{
if(openFileDialog1.ShowDialog()==DialogResult.OK)
{
wCount++;
doc=new Form1();
doc.Text=openFileFileDialog1.FileName;
doc.source.LoadFile(openFileFileDialog1.FileName, RichTextBoxStreamType.PlainText);
doc.Show();
}
}
private void fontMenu_Click(object sender, EventArgs e)
{
if(fontDialog1.ShowDialog()==DialogResult.OK&&doc!=null)
{
doc.source.SelectionFont=fontDialog1.Font;
}
}
private void colorMenu_Click(object sender, EventArgs e)
{
if(colorDialog1.ShowDialog()==DialogResult.OK&&doc!=null)
{
doc.source.SelectionColor=colorDialog1.Color;
}
}
private void btnBrowse_Click(object sender, EventArgs e)
{
if(folderBrowseDialog1.ShowDialog()==DialogResult.OK)
{
txtPosition.Text=folderBrowseDialog1.SelectedPath;
docPosition=txtPosition.Text;
}
}
3. XML文档编程:
对于结构化程度不高的数据,应用程序可以存储为文本文件或二进制文件,而结构化程度比较高的数据一般通过数据库来存储。为了统一互联网上的数据格式,又推出XML(Extensible Markup Language)标准。
XML具有严格的语法规范和良好的可扩展性,允许用户自定义描述数据的结构,不关心数据显示的方式。一个标准的XML文档由文档头部与文档主体构成,文档头部包括语句的声明,一般格式为:
<?xml version="1.0" encoding="UTF-8" ?>
而文档主体由若干个元素标记而成,仅有一个根元素,其他元素包含在根元素之内,称为子元素;每个元素都有开始标记和结束标记,开始标记为“<标记名>”,结束标记为“</标记名>”。XML区分大小写,必须保证元素的开始标记和结束标记的大小写一致。XML元素与属性之间使用空格分隔,使用“<!--内容-->”为注释。
1)XmlDocument类:
.NET中,创建XML文档主要有文档对象模型DOM(Document Object Model)和XmlTextWriter,常用DOM。DOM把XML文档加载到内存中,并转换为一种树状结构,然后再访问或修改树的节点。
DOM树在.NET中主要通过XmlDocument类的对象实现,该类位于System.Xml命名空间。DOM树上的每个节点对应着XMLNode类的对象,文档对象指向树的根节点,其他节点对象都是文档对象的子节点对象。因此,在创建、修改、删除、查询DOM树的节点对象时,都要先通过XmlDocument类的DocumentElement属性查询到DOM树的根节点对象,再通过根对象实现其他操作。
XmlDocument类常用属性:
属性 | 类型 | 说明 |
---|---|---|
DocumentElement | XmlElement | 获取XML文档的根元素 |
ParentNode | XmlNode | 获取当前节点的父节点 |
NodeType | XmlNodeType | 获取当前节点的类型 |
方法 | 返回值类型 | 说明 |
---|---|---|
CloneNode() | XmlNode | 用户单击对话框上的“打开”或“保存”按钮时触发 |
CreateAttribute() | XmlAttribute | 所有属性重置为默认值 |
CreateComment() | XmlComment | 显示对话框 |
CreateElement() | XmlElement | 创建新的元素 |
CreateNode() | XmlNode | 创建新的节点 |
GetElementById() | XmlElement | 获取指定id的元素 |
LoadXml() | void | 从指定的字符串中加载XML文档 |
Save() | void | 保存XML文档 |
Load() | void | 以流方式加载XML文档 |
XmlDocument doc=new XmlDocument();
XmlDeclaration declare=doc.CreateXmlDeclaration("1.0", "utf-8", "yes");
doc.AppendChild(declare);
root=doc.CreateElement("students");
doc.AppendChild(root);
XmlElement student=doc..CreateElement("student");
XmlElement name=doc..CreateElement("name");
XmlAttribute sid=doc.CreateAttribute("sid");
sid.Value=txtNum.Text;
name.Attributes.Append(sid);
XmlText nametext=doc.CreateTextNode(txtName.Text);
name.AppendChild(nametext);
XmlText agetext=doc.CreateTextNode(txtAge.Text);
age.AppendChild(agetext);
student.AppendChild(name);
student.AppendChild(age);
root.student.AppendChild(student);
上述代码创建了如下的XML文档:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<students>
<student>
<name sid="1111">111</name>
<age>11</age>
</student>
</students>
3)XML文档的查询:
.NET提供了包括XMLTextReader、Xpath、DOM等技术读取XML文档,其中XMLTextReader提供了非缓存的只读的访问方式,Xpath支持路径查询。DOM方式查询示例:
XmlDocument doc=new XmlDocument();
doc.Load(@"C:\Users\xml.txt");
XmlNodeList studentlist=doc.GetElementsByTagName("name");
foreach(var n in namelist)
{
XmlElement sname=(XmlElement)n;
if(name==sname.InnerText)
{
string id=sname.Attributes["sid"].Value;
string ret=string.Format("id:{0}, name:{1}", id, name);
lShow.Text=ret;
break;
}
lShow.Text="该学生不存在";
}
4)XML文档的编辑:
.NET提供了Xpath、DOM、Xquery三种技术实现XML文档的添加、修改、删除功能。使用DOM方式,用CreateElement()方法新建一个元素,然后为元素的属性和文本赋值,最后添加到父节点上;文档修改使用ReplaceChild()方法;文档的删除使用RemoveChild()方法。示例:
XmlNodeList studentlist=root.GetElementsByTagName("student");
XmlElement student=(XmlElement)studentlist[i];
txtID.Text=student.ChildNodes[0].Attributes["sid"].Value;
txtName.Text=student.ChildNodes[0].InnerText;
txtAge.Text=student.ChildNodes[1].InnerText;
//
root.RemoveChild(root.ChildNodes[cur-1]);
root.ReplaceChild(newstudent, root.ChildNodes[cur-1]);
五、网络编程:
现在的计算机以通信设备和线路相互连接,并配以对应的网络软件实现传输数据及资源共享,形成计算机网络。计算机网络按照通信距离划分,可以分为局域网、城域网、广域网和互联网,其中互联网可以实现全球范围的数据传输和共享。随着Internet的发展,很多实际应用都要涉及网络编程。
1. 网络分层协议与IP地址:
OSI把网络通信从底向上分为物理层、数据链路层、网络层、传输层、会话层、表示层、应用层7层,其中物理层为网络通信的传输介质,由连接不同节点的电缆和设备构成;数据链路层定义了如何让格式化的数据进行传输,以及如何控制对物理介质的访问,通常还提供错误检测和纠正,以保证可靠传输,该层数据以帧为单位进行传输;网络层为数据节点之间的传输创建逻辑链路,通过路由算法使数据包选择最适当的路径,以及实现拥塞控制等功能,该层用到网络互联协议IP
在互联网上的每台计算机都需要一个唯一的地址,称为IP地址,目前的IP地址是一个32位的二进制数,称为IPV4,为了便于识别记忆,一般采用用点分隔的十进制数表示,把32位二进制数分成4组,组之间用点分开,如192.168.0.1。而互联网上的每一种资源使用统一资源标识符URI
[protocol:]//domain[port]/[path]
其中,protocol为应用层使用协议;domain为资源地址,一般是域名,也可以是IP地址;port为端口号;path为资源在服务器上的存储路径。
2. 套接字:
套接字Socket开始只是UNIX系统中一个网络编程接口,用于实现网络上的数据收发。后来,微软公司也开发了一套基于TCP/IP协议的用于实现网络数据收发的库函数,这就是Windows套接字。
套接字分为流式套接字、数据报套接字。流式套接字也称面向连接的套接字,是基于TCP协议的,这种模式下需要先建立连接,连接成功再进行通信,而且还对数据进行检验,适用于要求数据安全性较高的场合,如使用FTP进行数据传输等。首先,服务器开启套接字绑定本地地址并监听一个固定的端口,然后客户端请求连接到服务器,该服务器接受客户端的连接,当连接创建成功之后进行数据的发送和接收,等到数据传输完成后,服务器端和客户端断开连接并释放资源。
数据报套接字是基于UDP协议的,这是一种不面向连接的协议,在数据发送和接收过程中可能会出现数据丢失、数据重复、数据无序等问题,适用于安全性要求不高但数据率较高的情况,如实时语音传输等。首先服务器开启套接字绑定本地地址,然后客户端发送连接请求,即开始发送与接收数据,传输完毕即断开连接,释放资源。
原始套接字在实用中用得较少,主要目的是对新网络进行测试,如对底层ICMP协议的直接访问等。
一个设备有216个,也就是65536个端口,每个端口对应一个唯一的程序。每个网络程序,无论是客户端还是服务器端,都对应一个或多个特定的端口号。由于0-1024之间多被操作系统占用,所以实际编程时一般采用1024以后的端口号。按端口号可分为3大类
· 公认端口(WellKnownPorts):从0到1023,它们紧密绑定(binding)于一些服务,通常这些端口的通讯明确表明了某种服务的协议。例如:80端口实际上总是HTTP通讯。
· 注册端口(RegisteredPorts):从1024到49151,它们松散地绑定于一些服务,也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。例如:许多系统处理动态端口从1024左右开始。
· 动态和/或私有端口(Dynamicand/orPrivatePorts):从49152到65535,理论上不应为服务分配这些端口,实际上机器通常从1024起分配动态端口。
3. 网络编程常见类:
.NET在System.Net命名空间下提供了各种网络协议编程的接口,封装了包括IPAddress、IPEndPort、Dns、WebClient等多个用于网络通信的类。
1)IPAddress类:
IPAddress类提供对IP地址的处理及转换功能,常用属性:
属性 | 类型 | 说明 |
---|---|---|
Broadcast | IPAddress | 提供IP广播地址 |
None | IPAddress | 不使用任何网络的IP地址 |
Loopback | IPAddress | 提供IP回环地址 |
方法 | 返回值类型 | 说明 |
---|---|---|
Parse() | IPAddress | 将IP地址字符串转换为IPAddress对象 |
GetAddressBytes() | byte[] | 将IPAddress转换为字节数组 |
GetHashCode() | int | 返回IP地址的哈希值 |
互联网上,TCP/IP使用IP地址和端口号来唯一标识一个设备和服务,统称端点。IPEndPort类提供了将端点表示为IP地址和端口号的功能。常用属性:
属性 | 类型 | 说明 |
---|---|---|
Address | IPAddress | 获取IP地址 |
AddressFamily | AddressFamily | 获取IP地址族 |
Port | int | 获取端口号 |
方法 | 返回值类型 | 说明 |
---|---|---|
Create() | IPAddress | 将IP地址字符串转换为IPAddress对象 |
ToString() | string | 返回指定端点的IP地址和端口号 |
GetHashCode() | int | 返回IP地址的哈希值 |
Dns类提供简单的域名解析功能,可把域名地址解析为IP地址,也可把IP地址解析为域名地址。Dns类中常用的GetHostName()方法可获取主机名,GetHostEntry()方法可将IP地址转换为OPHostEntry实例,Get
4)WebClient类:
WebClient类提供用于将数据发送到由URI标识的资源及从这样的资源接收数据的常用方法。
WebClient类常用属性:
属性 | 类型 | 说明 |
---|---|---|
BaseAddress | string | 获取或设置WebClient基于URI发出的请求 |
Encoding | Encoding | 获取和设置用于上传和下载字符串的编码 |
IsBusy | bool | 了解是否存在进行中的Web请求 |
方法 | 返回值类型 | 说明 |
---|---|---|
DownloadData() | byte[] | 从服务器下载数据 |
DownloadFile() | void | 将具有指定URI的资源下载到本地文件中 |
OpenRead() | Stream | 用指定URI的资源下载的数据打开一个可读的流 |
OpenWrite() | Stream | 打开一个流以将数据写入指定的资源 |
UploadData() | byte[] | 将数据缓冲区上传到由URI标识的资源 |
UploadFile() | byte[] | 将指定的本地文件上传到具有指定URI的资源 |
using System;
using System.Windows.Form;
using System.Net;
namespace socketPro
{
public partial class Form1: Form
{
public Form1()
{
InitializeComponent();
}
private void btnLocal_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
string hostName=Dns.GetHostName();
IPHostEntry myhostIPs=Dns.GetHostEntry(hostName);
listBox1.Items.Add("本机名:"+hostName);
listBox1.Items.Add("本机所有IP地址:");
listBox1.Items.AddRange(myhostIPs.AddressList);
}
private void btnServer_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
IPHostEntry serverIPs=Dns.GetHostEntry(txtDomain.Text);
listBox1.Items.Add("服务器名:"+serverIPs.HostName);
listBox1.Items.Add("服务器IP地址:");
listBox1.Items.AddRange(serverIPs.AddressList);
}
private void btnPort_Click(object sender, EventArgs e)
{
listBox1.Items.Clear();
IPAddress ip=IPAddress.Parse("192.168.0.1");
IPEndPoint iport=new IPEndPoint(ip,80);
listBox1.Items.Add("端点:"+iport.ToString());
}
private void btnDown_Click(object sender, EventArgs e)
{
WebClient client=new WebClient();
SaveFileDialog s=new SaveFileDialog();
if(s.ShowDialog()==DialogResult.OK)
{
string fileName=s.FileName;
client.DownloadFile(txtDomain.Text, fileName);
}
}
}
}
4. System.Net.Sockets命名空间中相关类:
1)用Socket类编程:
Socket类位于System.Net.Sockets命名空间,主要负责网络文件数据的相互接收及发送,常用属性:
属性 | 类型 | 说明 |
---|---|---|
AddressFamily | AddressFamily | 获取套接字地址族 |
Connected | bool | 确定是否连接上远程资源 |
LocalEndPoint | EndPoint | 获取本地端点 |
ReceiveBufferSize | int | 获取或设置接收信息的缓冲区大小 |
SendBufferSize | int | 获取或设置发送信息的缓冲区大小 |
方法 | 返回值类型 | 说明 |
---|---|---|
Accept() | Socket | 创建新的连接 |
Bind() | void | 使套接字与本地端点关联 |
Connect() | void | 建立与远程主机的连接 |
Receive() | int | 从绑定的套接字接收数据并存入缓冲区 |
Send() | int | 将数据发送到绑定的套接字中 |
SendFile() | void | 将文件发送到绑定的套接字中 |
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Form;
namespace socketDemo
{
public partial class Form1: Form
{
public Form1()
{
InitializeComponent();
}
private void btnSend_Click(object sender, EventArgs e)
{
string server=txtPah.Text;
string request="GET/HTTP/1.1\r\nHost:"+server+"\r\nUser-Agent:
Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/
10.2.2\r\nAccept-Language: zh-cn,zh;q=0.5\r\nAccept-Encoding:
gzip, deflate\r\nConnection:Keep-Alive\r\n\r\n";
IPHostEntry host=Dns.GetHostEntry(server);
IPAddress ip=host.AddressList[0];
richTextBox1.Text=GetSocket(request,ip);
}
private string GetSocket(string request, IPAddress ip)
{
string result="";
byte[] message=Encoding.UTF8.GetBytes(request);
byte[] receives=new Byte[1024];
try
{
int port=80;
IPEndPoint endPoint=new IPEndPoint(ip, port);
Socket s=new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
s.Connect();
if(s.Connected)
{
s.Send(messages, messages.Length, 0);
result=Encoding.UTF8.GetString(receives, 0, s.Receive(receives, receives.Length, 0));
}
}
catch(Exception ex)
{
result=ex.Message;
}
}
}
}
2)用TCP协议编程:
TCP协议编程需要用到的TcpListener类和TcpClient类位于System.Net.Sockets命名空间,其中封装了Socket类进行通信的方法,TcpListener类用于监听和接收连接请求,TcpClient类用于连接、发送、接收网络数据。
TcpListener类常用的属性:
属性 | 类型 | 说明 |
---|---|---|
Active | bool | 是否在监听 |
Server | Socket | 获取套接字 |
LocalEndPoint | EndPoint | 获取当前端点 |
方法 | 返回值类型 | 说明 |
---|---|---|
AcceptSocket() | Socket | 接收套接字挂起的请求 |
AcceptTcpClient() | TcpClient | 接收客户端挂起的请求 |
Start() | void | 开始监听 |
Stop() | void | 停止监听 |
BeginAcceptTcpClient() | IAsyncResult | 开启异步操作并接收新的连接 |
EndAcceptTcpClient() | TcpClient | 结束异步操作 |
属性 | 类型 | 说明 |
---|---|---|
Active | bool | 确认是否建立连接 |
Client | Socket | 获取或设置套接字 |
Connected | bool | 确认是否已连接 |
方法 | 返回值类型 | 说明 |
---|---|---|
Connect() | void | 连接到服务器 |
GetStream() | NetworkStream | 获取发送及接收数据的网络流 |
Close() | void | 关闭TCP连接并释放资源 |
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Form;
namespace chatServer
{
public partial class Form1: Form
{
public static TcpClient client=null;
public static NetWorkStream stream=null;
public static TcpListener listener=null;
public Form1()
{
InitializeComponent();
}
private void btnSend_Click(object sender, EventArgs e)
{
byte[] message=Encoding.UTF8.GetBytes(rtxtContent.Text);
stream=client.GetStream();
stream.Write(messages, 0, messages.Length);
}
private void btnReceive_Click(object sender, EventArgs e)
{
byte[] receives=new byte[1024];
stream=client.GetStream();
stream.Read(receives, 0, receives.Length);
string datas=Encoding.UTF8.GetString(receives);
listBox1.Items.Add("客户端:"+datas);
}
private void btnStop_Click(object sender, EventArgs e)
{
if(stream!=null) stream.Close();
if(client!=null) client.Close();
if(listener!=null) listener.Stop();
}
private void btnStart_Click(object sender, EventArgs e)
{
IPEndPoint iep=new IPEndPoint(IPAddress.Any, int.Parse(txtport.Text));
listener=new TcpListener(iep);
listener.Start();
listBox1.Items.Add("服务器已开启");
client=listener.AcceptTcpClient();
listBox1.Items.Add("客户端已连上");
}
}
}
客户端示例代码:
using System;
using System.Net.Sockets;
using System.Text;
using System.Windows.Form;
namespace chatClient
{
public partial class Form1: Form
{
public static TcpClient client=null;
public static NetWorkStream stream=null;
public Form1()
{
InitializeComponent();
}
private void btnSend_Click(object sender, EventArgs e)
{
byte[] message=Encoding.UTF8.GetBytes(rtxtContent.Text);
stream=client.GetStream();
stream.Write(messages, 0, messages.Length);
}
private void btnReceive_Click(object sender, EventArgs e)
{
byte[] receives=new byte[1024];
stream=client.GetStream();
stream.Read(receives, 0, receives.Length);
string datas=Encoding.UTF8.GetString(receives);
listBox1.Items.Add("服务器:"+datas);
}
private void btnStart_Click(object sender, EventArgs e)
{
string serverIp=txtServer.Text;
int port=int.Parse(txtPort.Text);
client=new TcpClient(serverIp, port);
}
private void btnStop_Click(object sender, EventArgs e)
{
if(stream!=null) stream.Close();
if(client!=null) client.Close();
}
}
}
当接收或发送的数据量很大时,上述同步方式就比较困难,会出现阻塞及资源闲置等问题。Socket类、TCPListener类及TCPClient类都提供了异步的操作方式来解决此类问题。
3)用UDP协议编程:
UDP协议是面向非连接的协议,.NET在Syetem.Net.Sockets命名空间中用UdpClient类封装了UDP套接字。UdpClient类的常用属性:
属性 | 类型 | 说明 |
---|---|---|
Active | bool | 确认是否建立连接 |
Client | Socket | 获取或设置套接字 |
EnableBroadcast | bool | 确认是否可以接收和发送广播数据包 |
方法 | 返回值类型 | 说明 |
---|---|---|
Connect() | void | 连接到服务器 |
GetStream() | NetworkStream | 获取发送及接收数据的网络流 |
Close() | void | 关闭UDP连接并释放资源 |
Receive() | byte[] | 接收数据 |
Send() | int | 发送数据 |
JoinMulticastGroup() | void | 加入组播 |
DropMulticastGroup() | void | 推出组播 |
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Form;
namespace UdpTest
{
public partial class Form1: Form
{
public Form1()
{
InitializeComponent();
}
private void btnSend_Click(object sender, EventArgs e)
{
UdpClient client=new UdpClient();
client.EnableBroadcast=true; //组播地址必须为224.0.0.0~239.255.255.255
IPEndPoint ip=new IPEndPoint(IPAddress.Parse("225.0.0.1"), 35);
byte[] sendMessages=Encoding.UTF8.GetBytes(txtContent.Text);
client.Send(sendMessages, sendMessages.Length, ip);
txtContent.Clear();
txtContent.Focus();
}
}
}
接收端示例代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Form;
namespace UdpTest2
{
public partial class Form1: Form
{
delegate void MessagesCallback(string data); //定义一个委托
MessagesCallback onMessagesCallback; //定义一个事件
public UdpClient client;
public Form1()
{
InitializeComponent();
onMessagesCallback=new MessagesCallback(AddData); //绑定事件方法
}
public void AddData(string data)
{
if(rtxtContent.InvokeRequired)
{
this.Invoke(onMessagesCallback, data); //触发事件
}
else rtxtContent.AppendText(data);
}
private void Form1_Load(object sender, EventArgs e)
{
client=new UdpClient();
client.JoinMulticastGroup(IPAddress.Parse("225.0.0.1")); //加入组播
client.Ttl=30;
IPEndPoint server=null;
while(true)
{
byte[] Rmessages=client.Receive(ref server);
string data=Encoding.UTF8.GetString(Rmessages);
AddData(data);
}
}
}
}
5. System.Net.Mail命名空间中相关类:
电子邮件在现在生活中已经不可或缺,发送及接收电子邮件也称为程序中的需要。电子邮件一般是通过简单邮件传输协议SMTP(Simple Mail Transfer Protocol)服务器来发送,该协议默认端口号25,使用用户名和密码方式进行验证。在SMTP中,电子邮件包括信封、首部和正文,信封包括发信人和收信人的电子邮件地址;首部包括邮件主题、附件、发邮件时间等;正文是邮件的主体。
1)发送邮件:
System.Net.Mail命名空间中提供了邮件处理的类,使邮件的发送和接收变得简单,其中常用的类有MailAddress、MailMessage、SmtpClient。
MailAddress类常用的属性:
属性 | 类型 | 说明 |
---|---|---|
Address | string | 获取邮件地址 |
Host | string | 获取主机 |
User | string | 获取用户名 |
方法 | 返回值类型 | 说明 |
---|---|---|
MailAddress() | void | 创建一个新实例 |
属性 | 类型 | 说明 |
---|---|---|
Body | string | 获取或设置正文 |
BodyEncoding | Encoding | 获取或设置正文编码方式 |
From | MailAddress | 获取或设置发件人信息 |
Subject | string | 获取或设置主题 |
SubjectEncoding | Encoding | 获取或设置主题编码方式 |
方法 | 返回值类型 | 说明 |
---|---|---|
MailMessage() | void | 创建一个新实例 |
Dispose() | void | 释放资源 |
属性 | 类型 | 说明 |
---|---|---|
Credentials | ICredentialsByHost | 获取或设置验证方式 |
DeliveryMethod | SmtpCredentialsMethod | 获取或设置发送的方式 |
Host | string | 获取或设置主机信息 |
Port | int | 获取或设置端口号 |
UseDefaultCredentials | bool | 获取或设置是否使用默认的验证方式 |
Timeout | int | 获取或设置超时 |
方法 | 返回值类型 | 说明 |
---|---|---|
SmtpClient() | void | 创建一个新实例 |
Send() | void | 发送邮件信息 |
Dispose() | void | 释放资源 |
private void btnSend_Click(object sender, EventArgs e)
{
MailAddress from=new MailAddress(txtSend.Text);
MailAddress to=new MailAddress(txtRec.Text);
MailMessage message=new MailMessage(from, to);
message.SubjectEncoding=Encoding.UTF8;
message.Subject=txtSubject.Text;
message.BodyEncoding=Encoding.UTF8;
message.Body=rtxtContent.Text;
Attachment attach=new Attachment(txtPath.Text);
SmtpClient client=new SmtpClient("smtp."+from.Host);
client.UseDefaultCredentials=false;
client.Credentials=new NetworkCredential(from.Address, txtPwd.Text); //用户名和密码方式验证
client.DeliveryMethod=SmtpDeliveryMethod.Network;
try
{
client.Send(message);
}
catch
{
MessageBox.Show(ex.Message);
}
}
2)接收邮件:
接收邮件一般用POP3(Post Office Protocol Version 3)协议,默认端口号110.当客户端与服务器成功连接后,即可对邮件进行操作。POP3服务器向客户端发送一个确认信息,该消息由一个状态码和命名组成,其中状态码为“确定”则命名为“+OK”,状态码为“失败”则命名为“-ERR”。客户端根据这个消息决定下一步操作,服务器每收到一个客户端的消息都会发送一个状态码判断是否正确。
Cursor.Current=Cursor.WaitCursor;
int index=txtUser.Text.IndexOf('@');
string server="pop3."+txtUser.Text.Substring(index+1);
pop3=new POP3Help(server, 110, txtUser.Text, txtPwd.Text);
pop3.Connect();
......
richTextBox1.Text=pop3.GetMail(listBox1.SelectedIndex+1);
pop3.DeleteMail(listBox1.SelectedIndex+1);
......
pop3.Close();
六、GDI+图形图像操作:
WinForm中的图形通过GDI+实现。GDI+是.NET Framework的二维图形、图像处理子系统,提供了图形图像操作的应用程序接口,使用GDI+可以在屏幕上或打印机上显示信息,可以用来绘制各种数据图形。
1. Graphics类:
Graphics类是GDI+的核心,Graphics对象表示GDI+绘图表面,提供将对象绘制到显示设备的方法。创建Graphics对象有三种方法:
① 在窗体或控件的Paint事件中创建:
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphice g=e.Graphics;
}
② 调用控件或窗体的CreateGraphics()方法以获取对Graphics对象的引用:
private void Form1_Load(object sender, EventArgs e)
{
Graphice g;
g=this.CreateGraphics();
}
如果在已存在的窗体或控件上绘图要使用这种方法。
③ 由从Image继承的任何对象创建Graphics对象:
private void Form1_Load(object sender, EventArgs e)
{
Bitmap mbit=new Bitmap(@"C:\ls.bmp");
Graphics g=Graphics.FromImage(mbit);
}
此方法在需要更改已存在的图像时十分有用。
2. 画笔与画刷:
① 设置画笔Pen:
示例:创建一个Pen对象,颜色蓝,宽度2:
Pen mypen1=new Pen(Color.Blue,2);
设置的画笔的粗细值小于等于0,就按默认值1来设置。
可以用来定义颜色的Color结构中的颜色属性:
属性 | 说明 | 属性 | 说明 | 属性 | 说明 |
---|---|---|---|---|---|
Black | 黑 | Green | 绿 | Pink | 粉红 |
Blue | 蓝 | LightGray | 浅灰 | Red | 红 |
Cyan | 青 | Magenta | 洋红 | White | 白 |
Gray | 灰 | Orange | 橘黄 | Yellow | 黄 |
画刷主要用于填充几何图形。Brush类是一个抽象基类,不能实例化,如果要创建一个画笔对象,需使用从Brush派生出的类,如SolidBrush、HatchBrush等。
· SolidBrush类:
用于定义单色画笔,用以填充图形形状,如矩形、椭圆、扇形、多边形和封闭路经。示例代码:
private void button1_Click(object sender, EventArgs s)
{
Graphics ghs=this.CreateGraphics();
Brush mybs=new SolidBrush(Color.Red);
Rectangle rt=new Rectangle(10,10,100,100);
ghs.FillRectangle(mybs,rt);
}
当不再需要返回Graphics时,必须通过调用其Dispose()方法来释放,Graphics只在当前窗口消息期间有效。
· HatchBrush类:
提供了一种特定样式的图形用来制作填满整个封闭区域的绘图效果,位于System.Drawing.Drawing2D命名空间下。示例代码:
private void button1_Click(object sender, EventArgs s)
{
Graphics ghs=this.CreateGraphics();
for(int i=1;i<6;i++)
{
HatchStyle hs=(HatchStyle)(5+i);
HatchBrush hb=new HatchBrush(hs,Color.Write);
Rectangle rt=new Rectangle(10,50*i,50*i,50);
ghs.FillRectangle(hb,rt);
}
}
其中,HatchStyle表示此HatchBrush所绘制的图案。
· LinearGradientBrush类:
提供一种渐变色彩的特效,填满图形的内部区域。示例代码:
private void button1_Click(object sender, EventArgs s)
{
Point p1=new Point(100,100);
Point p2=new Point(150,150);
LinearGradientBrush lgb=new LinearGradientBrush(p1,p2,Color.Black,Color.Write);
Graphics ghs=this.CreateGraphics();
lgb.WrapMode=WrapMode.TileFlipX;
ghs.FillRectangle(lgb,15,15,150,150);
}
其中,p1为渐变的开始点,p2为渐变的结束点,后面的颜色分别为渐变的开始和结束的颜色。
3. 基本图形绘制:
① 绘制直线:
调用Graphics类的DrawLine()方法,结合Pen对象,可以绘制直线,有两种结构。
public void DrawLine(Pen pen,Point p1,Point p2);
public void DrawLine(Pen pen,int x1,int x2,int y1,int y2);
示例代码:
private void button1_Click(object sender, EventArgs s)
{
Pen blackPen=new Pen(Color.Black, 3);
Point p1=new Point(10,50);
Point p2=new Point(100,50);
Graphics g=this.CreateGraphics();
g.DrawLine(blackPen,p1,p2);
}
② 绘制矩形:
通过Graphics类的DrawRectangle()方法,可以绘制矩形,矩形由坐标对、宽度、高度指定。示例代码:
private void button1_Click(object sender, EventArgs s)
{
Graphics g=this.CreateGraphics();
Pen myPen=new Pen(Color.Black, 8);
g.DrawRectangle(myPen,10,10,150,100);
}
③ 绘制椭圆:
通过Graphics类的DrawEllipse()方法,可以绘制椭圆,椭圆由一对坐标、宽度、高度指定,坐标指定椭圆边框的左上角。示例代码:
private void button1_Click(object sender, EventArgs s)
{
Graphics g=this.CreateGraphics();
Pen myPen=new Pen(Color.Black, 3);
g.DrawEllipse(myPen,100,5,100,50);
}
④ 绘制圆弧:
通过Graphics类的DrawArc()方法,可以绘制圆弧,圆弧由圆弧边界矩形、起始角、宽度角指定。示例代码:
private void button1_Click(object sender, EventArgs s)
{
Graphics g=this.CreateGraphics();
Pen myPen=new Pen(Color.Black, 3);
Rectangle rec=new Rectangle(70,20,100,60);
g.DrawArc(myPen,rec,210,210);
}
⑤ 绘制扇形:
通过Graphics类的DrawPie()方法,可以绘制扇形,扇形由一个坐标对、宽度、高度以及起始角、宽度角所指定。示例代码:
private void button1_Click(object sender, EventArgs s)
{
Graphics g=this.CreateGraphics();
Pen myPen=new Pen(Color.Black, 3);
g.DrawPie(myPen,50,50,120,100,210,120);
}
扇形是坐标及宽度、高度指定的矩形的内切椭圆弧的一部分。
⑥ 绘制多边形:
多边形是有3条或更多直边的闭合图形,绘制需要Graphics对象、Pen对象和Point对象数组,使用Graphics类的DrawPolygon()方法。示例代码:
private void button1_Click(object sender, EventArgs s)
{
Graphics g=this.CreateGraphics();
Pen myPen=new Pen(Color.Black, 3);
Point p1=new Point(80,20);
Point p2=new Point(40,50);
Point p3=new Point(80,80);
Point p4=new Point(160,80);
Point p5=new Point(200,50);
Point p6=new Point(160,20);
Point[] pArray={p1,p2,p3,p4,p5,p6};
g.DrawPolygon(myPen,pArray);
}
如果多边形数组中的最后一个点和第一个点不重合,则这两个点指定多边形的最后一条边。
⑦ 绘制文本:
通过Graphics类的DrawString()方法,可以指定位置以指定的Brush和Font对象绘制指定的文本字符串。示例代码:
private void Form1_Paint(object sender, PaintEventArgs s)
{
string str="柱状图";
Font myFont=new Font("宋体",16,FontStyle.Bold);
SolidBrush mybs=new SolidBrush(Color.Black);
Graphics g=this.CreateGraphics();
g.DrawString(str,myFont,myBrush,60,20);
}
其中的坐标为绘制文本的左上角坐标。
⑧ 绘制图像:
使用DrawImage方法可以在由一对坐标指定的位置以图像的原始大小或者指定的大小绘制图像,有两种格式。
public void DrawImage(Image imagen,int x,int y);
public void DrawImage(Image imagen,int x,int y,int width,int height);
示例代码:
private void Form1_Paint(object sender, PaintEventArgs s)
{
Image img=Image.FromFile("logo.jpg");
Graphics g=this.CreateGraphics();
g.DrawImage(img,50,20,90,92);
}
七、打印技术:
Windows提供了一组打印控件,包括PageSetupDialog、PrintDialog、PrintDocument、PrintPreviewControl和PrintPreviewDialog,开发人员可以直接使用这些控件控制打印的文本和数据格式。
1. PageSetupDialog控件:
用于设置页面详细信息,允许用户设置边框和边距调整量、页眉和页脚,以及纵向、横向打印。控件属性:
属性 | 说明 |
---|---|
Document | 获取页面设置的PrintDocument类对象 |
AllowMargins | 是否启用对话框的边距部分 |
AllowOrientation | 是否启用对话框的方向部分 |
AllowPager | 是否启用对话框的纸张部分(纸张大小和来源) |
AllowPrinter | 是否启用打印机按钮 |
2. PrintDialog控件:
用于选择打印机和要打印的页,并确定其他与打印相关的设置,比如选择全部打印、打印选定的页范围或打印选定内容。常用属性:
属性 | 说明 |
---|---|
Document | 获取PrinterSettings类的PrintDocument类对象 |
AllowCurrentPage | 是否显示“当前页”选项按钮 |
AllowPrintToFile | 是否启用“打印到文件”复选框 |
AllowSelection | 是否启用“选择”选项按钮 |
AllowSomePages | 是否启用“页”选项按钮 |
3. PrintDocument控件:
设置打印的文档,比较常用的是PrintPage事件和Print()方法,其中PrintPage事件在需要为当前页打印的输出时发生,调用Print()方法开始文档的打印进程。示例代码:
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
//通过GDI+绘制打印文档
e.Graphics.DrawString("文本标题",new Font("宋体",15),Brushes.Black,350,80);
e.Graphics.DrawLine(new Pen(Color.Black),(float)3.00),100,185,720,185);
e.Graphics.DrawString("文本内容",new Font("仿宋",12),Brushes.Black,110,195);
//.......
}
private void button1_click(object sender, EventArgs e)
{
if(MessageBox.Show("是否需要预览打印文档","打印预览",MessageBoxButtons.YesNo)==DialogResult.Yes)
{
this.printPreviewDialog1.UseAntiAlias=true; //开启系统反锯齿功能
this.printPreviewDialog1.Document=this.printDocument1; //设置要预览的文档
this.printPreviewDialog1.ShowDialog(); //打开预览窗口
}
else
{
this.printDocument1.Print(); //调用Print()方法直接打印文档
}
}
4. PrintPreviewControl控件:
为用户提供一个预览功能。示例代码:
private void Form1_Load(object sender,EventArgs e)
{
printPreviewControl1.Document=printDocument1; //设置要预览的文档
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
//声明一个string类型变量用于存储图片位置
string str=Application.StartupPath.Substring(0,Application.StartupPath.Substring(0,Application.StartupPath.LastIndexOf("\\").LastIndexOf("\\"));
str+=@"\img.jpg";
e.Graphics.DrawImage(Image.FromFile(str),10,10,607,452);
}
5. PrintPreviewDialog控件:
用于显示文档打印之后的外观,包含打印、放大、显示一页或多页以及关闭对话框按钮。常用属性:
属性 | 说明 |
---|---|
Document | 用于设置要预览的文档 |
UseAntiAlias | 用于设置打印是否使用操作系统的反锯齿功能 |
方法 | 说明 |
---|---|
ShowDialog() | 用来显示打印预览对话框 |