语法学习"/>
C#基础语法学习
1.NET 平台的体系结构
1.CLR:公共语言运行时
CLR在程序运行时的作用有内存管理,程序集加载,安全性,异常处理和现成同步等
2.FCL:框架类库
3.CTS:通用类系统
4.CLS:公共语言规范
5.IL:中间语言
6.JIT:即时
2.框架类库的命名空间
1.System.Web:提供可以使用浏览器与服务器通信的类和接口
2.System.Tesx:处理文本
3.System.IO:管理对文件和流程的操作
4.System.Conllections.Generic:包含定义泛型集合的接口和类
5.System.Conllections :包含定义各种对象集合的接口和类
6.System.Net:对网络协议进行编程
注意:
.NET Core 和.NET Framework 除了共有.NET Standard 类库外还有很多各自独有的类和类库
System.Web.Mvc: .NET Framework下创建的一个MVC项目,控制台命名空间
System.AspNetCore.Mvc: .NET Core框架下创建的一个MVC项目
2.语法基础(一)
2.1预定数据类型
主要类型
整型:byte, short, int ,int ,long
浮点型:float 默认值0.0f, double ,decimal
布尔型:bool 默认值false
字符型:char
字符串:string
2.2程序中的变量和常量
1.变量
语法:
访问修饰符(public...) 数据类型(string...) 变量名:
变量起名规则:
-
起名必须字母或下画线开头
-
只能由字母,数字,下画线组成
-
不能与关键字名称相同
-
是区分大小写的
2.常量
语法:
const 数据类型 常量名 =常量值
特征:
常量必须在声明时初始化,并且指定了值后,就不能再修改
常量的值不能用变量中的值来初始化
3.使用var创建隐型局部变量
UserInfo为用户自定义类型
var关键字声明变量的三个原则
-
var关键字声明变量必须包含一个初始化器,初始化器必须为一个表达式,且初始化器不能包含自身变量对象
-
var关键字声明的变量初始化值不能为null
-
不能在同一语言中初始化多个隐式类型的变量
2.3 表达式和常用运算符
-
算术运算符:+(加),-(减),*(乘),/(除),%(余),++(加1),--(减1),`(取反)
-
比较运算符:>(大于),<(小于),>=(大于或等于) , =<(小于或等于) ,==(是否相等), != (是否不相等)
-
逻辑运算符:&& (并),||(或) ,! (非)
-
快捷运算符:+= ,-= ,*= 、 /= ,%=
-
三元运算符:表达式? 表达式正确时要返回内容 : 表达式错误时要返回内容
2.4 C#中的程序控制结构
1.条件判断结构
1.简单if语法: if(判断表达式){代码块1}else{代码块2} 2.多重if结构: if(判断表达式1){代码块1 }else if(判断表达式2){代码块2 }else{代码块3 } 3.嵌套if结构:if(判断表达式1){if(判断表达式1.2){}else{}}else{if(判断表达式2){}else{}}
2.switch...case
C#中的switch与java中的语法差不多,区别就是C#要求default语句和每个case语句里面必须游break语句,否则编译报错。
语法:
switch(变量){ case 值1: 语句块1 breakcase 值2: 语句块2 breakcase 值3: 语句块3 break default: 语句块4 break }
3.循环结构
3.1.while循环
语法: while(条件表达式){ 代码块 }
3.2.do...while循环
先执行再判断,至少执行一次
语法: do{代码块 }while(条件表达式)
3.3.for循环
语法 for(表达式1;表达式2;表达式3;){ 代码块 } 表达式1:变量 表达式2:判断条件 表达式3:计算方式
3.4.foreach循环
语法: foreach(类型(集合或数组中每个元素的类型) 变量名 in 集合或数组){ 代码块 }
3.C#语法基础(二)
3.1.数组
1.2数组的使用场合以及System.Array类
属性 | 描述 |
---|---|
length | 得到数组所有维元素总个数的属性 |
实例方法 | 描述 |
AopyTo() | 将一个一维数组中所有元素复制到另一个一维数组中 |
GetLength() | 返回指定维的元素个数 |
GetValue() | 通过索引返回指定元素的值 |
SetValue() | 将数组中的指定元素设定为指定值 |
静态方法 | 描述 |
BinarySearch() | 使用二进制搜索方法搜索一维已排列数组中的某值 |
Clear() | 将数组中一组元素设为0或null |
Copy() | 将数组中的部分元素复制到另一个数组中 |
CreateInstance() | 初始化Array类实例 |
IndexOf() | 返回给定值在一维数组中首次出现的位置索引 |
LastIndexOf() | 返回给定值在一维数组中最后一次出现的位置索引 |
Reverse() | 反转给定一维数组中元素的顺序 |
Resize() | 将数组的大小更改指定的新大小 |
Sort() | 将数组中元素进行排序,只能对一维数组从小到大进行排序 |
定义数组语法: 数据类型 [数组长度] 数组名; 下标访问和赋值语法: //访问数组对应下标元素,将其值赋予一个变量 [数组数据类型] 变量名 =数组名[下标] //为数组对应下标元素赋值 数组名[下标]=值//创建隐式型数组(转化为同一数据类型不然就会报错)var 变量名=new[]{值1,值2.....}
注意:取值很容易犯索引超出数组界限的错误,下标是从0开始的。
3.2.枚举(自定义类型)
枚举类语法: public enum Color {Blue,red,Yellow,Pink, } 使用: using System; namespace EnumDemo {class Program{static void Main(string[] args){//定义一个Color变量,并赋值为color.abc,但由于Color枚举并没有abc,所以无法通过编译Color col=color.Blue;Console.WriteLine(col);}public enum Color{Blue,red,Yellow,Pink,}} }
枚举类型的优势:
-
枚举可以使代码更易于维护,更便于理解和阅读(团队开发的时候这一点非常重要)
-
枚举有助于确保给变量指定合法的,规定范围内的值。
枚举就是程序员定义的整数类型,没有特别指定每一个元素对应的整数,那么编译器默认就会把0映射到第一个元素上,然后对后续每一个元素加1递增。
3.3数值类型与字符串类型之间的类型转换
3.3.1.数值类型转换成字符串
C#默认用ToString()方法
int num=5; string a=num.ToString()
3.3.2.字符串转换成数值类型
这么把用户输入字符串变整数?使用int.Parse()
int num=int.Parse(Console.ReadLine());
4.类和对象的应用
4.1.面向对象语法基础(类,对象,成员方法)
//定义的类可以和Porgram类在同一页面也可以在不同页面,class之间是并列的能嵌套,嵌套会报错 using System; using System.Text; namespace Demo//包 {class Student //定义的Student类{public string id; //字段,变量public string name;public int age;//[访问修饰符] 返回值 方法名(参数列表)public void study()//成员方法(定义在类内部的方法,反映这个类具有的行为){Console.WriteLine("好好学习");}}class Program{static void Main(string[]args){//用类实例化一个对象,new关键字的作用就是实例化一个对象Student stu=new Student();//赋值stu.name="晓星尘"//对象名.方法名(参数列表)stu.study()//方法调用}} }
类和对象是不可分割的两个概念,类是共同点的归纳,是对一类事物的抽象概括,对象是具体的事物。
4.2.创建匿名类的对象
语法: var role=new{id=1,rolename="晓星尘"}
在创建匿名类的对象时,编译器首先为新对象创建一个类(类的名称由编译器指定),并在该类创建相应的属性。
4.3.构造方法,this关键字
语法: class 类名 { //构造方法名与类同名,没有返回值类型,如果写了编译报错public 类名(参数列表){//代码} } //1.无参构造方法(类自带的构造方法默认的数值为0,字符串为空) //实例化调用简单public 类名(){//代码} 类名 对象名=new 类名();//实例化 //带参构造方法(有了带参构造方法,就不会提供无参构造方法了) class 类名 {public string A;public int B;public 类名(string a,int b){this.A=a;//this就是给当前操作的对象进行赋值this.B=b} } class Program{static void Main(string[]args){类名 对象名=new 类名("小星星",12);//实例化}}
4.4.命名空间
//1.同一命名空间内Main()方法 namespace MyNameSpace//自定义命名空间 {class student{//代码 } } namespace Demo{static void Main(string[] args)//主方法{MyNameSpace.student stu=new MyNameSpace.student()} } //不同命名空间内Main()方法 using MyNameSpace //使用using关键字预先引用命名空间 namespace Demo {public void Main(string[]args){student stu=new student();} }
5.类和对象的高级应用
5.1.面向对象语言的特点
三个特点:
封装——将数据或函数(行为(方法))等集合在一个个的单元中(类)
继承。。。。
多态——一个事物(类)有多种表现形式
5.2.访问修饰符
访问修饰符 | 说明 |
---|---|
public | 公开的,无限制条件,任何代码都可以访问 |
internal | 可被同一个程序集的所有代码访问 |
protected | 可被自己或者子类代码访问 |
private | 私有的,只有自己的代码才能访问 |
5.3.值类型和应用类型
5.3.1.分类
值类型:int,float等
引用类型:string,自定义类,数组等
值类型是赋值的数据,对象却是赋值内存地址。
5.3.2.值类型和引用类型的转换:装箱和拆箱
装箱就是将值类型转换为引用类型。
int val=100; string str=val//隐式装箱 int num=int(str)//显示拆箱//经过装箱的才能进行拆箱
5.4.类的静态成员
成员变量用来存储数据,成员方法用来完成功能。
两种方法:
-
一种是成员方法,通过实例化对象,"对象名.方法名()"调用
-
另一种是 Console.ReadKey()方法,这种方法定义的时候前面都有一个static关键字。
静态方法 : 是属于整个类的,不针对某个对象,所以静态方法是通过类来调用的。(不用实例化对象,直接通过类名调用.)
类名.方法名(参数列表);
5.5.ref关键字和out关键字
1.ref关键字
回调用方法时,在方法中对形参的任何更改都将放映在该实参中
namespace Demo{ static void Main(string[] args){int val=0;Mthod(ref val)Console.WriteLine(val);}static void Mthod(ref int i){i=40;}} 结果:40
2.out关键字
传递到ref的参数必须最先初始化。而out则不同,out参数在传递之前不需要初始化。
ref关键字重在修改参数的数据,而out关键字在带回执行结果。
namespace Demo{class Program{static void Main(string[] args){int val;Mthod(out val)Console.WriteLine(val);}static void Mthod(out int i){i=40;} } 结果:40
5.6.成员方法重载
方法的重载,是一个类内部有多个同名的方法,根据方法参数的类型或者个数进行区分。
一个方法采用ref参数,而另一个方法采用out参数,则无法重载这两个方法;
构造函数也是方法重载
6.C#面向对象深入
6.1.类似于的数据类型——结构体
访问修饰符 struct 结构体 {定义结构体成员 } //类是引用类型数据,结构体是值类型数据 struct Student {public string name;public int age; } class Program {static void Main(tring[] args){Student stu//不需要new关键字创造对象stu.name="晓星尘"stu.age=12;Change(stu);Console.WriteLine(stu.name);Console.WriteLine(stu.age);}public static Change(Student s){s.name="小星星";s.age=67;} } //结果:晓星尘 12
类对象传递的是地址,修改了形参s,也就是修改了实参stu,结构体(值类型数据),修改形参s,也改变不了stu。
结构体和类的不同
结构体 | 类 |
---|---|
值类型 | 引用类型 |
不能有无参构造方法 | 可以有无参构造方法 |
创造对象可以不用new 关键字 | 创建对象必须用new关键字 |
不能被继承 | 可以被继承 |
6.2.访问私有成员的利器——属性
1.private私有成员变量,只有在本类内部调用的代码才可以访问它,造成public不安全,private不方便,为解决,C#提供了属性,通过属性读取和写入私有成员变量,以此对类中的私有成员变量进行保护。
语法: 访问修饰符 数据类型 属性名 {get{返回私有成员变量;}set{设置私有成员变量;} }//属性定义必须有数据类型,属性的数据类型与所保护的成员变量数据类型是一致的。列如: public int Age {get{return this.age;}set{this.age=value;} } public Person(int age)//为了方便对象访问修改属性 {this.age=age; } //与上面没有关联 //输出语句 Console.WriteLine("{0}岁",Age);
三种类型:
可读可写属性;
只读属性(省略set块);
只写属性(省略get块);
1.自动属性
语法: public string name{get;set;}
编译器回自动生成一个私有变量和默认的get/set操作
6.3.索引器
索引器是一种特殊类型的属性,把它添加到一个类中,以提供类似于数组的访问。
语法: [访问修饰符] 数据类型 this [数据类型 标识符] {get{....}set{....} } 例: using Sytem; using Sytem.Text;namespace Test {//Photo表示照片class Photo{private string _title;public Photo(string title){this._title=title;}public string Title//只读读写{get{return this._title;}}}//此类表示相册,即照片的集合class Album{//用于存储照片数组private Photo[] photos;public Album(int capascity){this.photos=new Photo[capacity];}public Photo this[int index]{get{//验证索引范围if(index<0 || index>=this.photos.Length){Console.WriteLine("索引无效");return null;//表示失败}return photos[index]//返回请求的照片}set{//验证索引范围if(index<0 || index>=this.photos.Length){Console.WriteLine("索引无效");return null;//表示失败}return photos[index]=value;//返回请求的照片}}public Photo this[string title]{get{//遍历数组中的所有照片foreach(Photo p in photos){if(p.Title==title){return p;}}Console.WriteLine("未找到");//使用null指示失败return null;}}}class TestIndex{static void Main(string[] args){//创建容量为3的相册Album friends=new Album(3);//创建3张照片Photo first=new Photo("Jenn");Photo second=new Photo("Smith");Photo third=new Photo("Mark");//向相册加载照片friends[0]=first;friends[1]=second;friends[2]=thrid;//按索引进行检索Photo Odj1=friend[2];Console.WritelLine(obj.Title);//按名称进行检索Photo odj2=friends["Jenn"];Console.WritelLine(obj2.Title);}} }
索引器可以重载,一个类有多个索引器。
优点:访问数组一样访问集合或类的数组。
6.4.静态类
类前面加个static
静态类 | 非静态类 |
---|---|
使用static关键字 | 不要static关键字 |
必须只有静态成员 | 可以包含静态成员 |
使用类名访问成员和方法 | 实例化对象才能访问非静态成员 |
不能被实例化 | 可以被实例化 |
不包含实例构造方法,只是静态构造方法 | 包含实例构造方法 |
7.委托,Lambda表达式和事件
7.1委托
对象包含数据,委托包含的只是方法的细节.
语法: [访问修饰符] delegate 返回值类型 委托名([参数列表])//注意:实例化委托时得参数仅仅是方法名,不能加“()”更不能传参。//定义委托 public delegate int Callback(int numl,int num1); class MathCalc {public int Plus(int number1,int number2){return number1+number2;} } class Program {public static void Main(string[] args){//实例化对象MathCalc mc=new MathCalc();//声明并实例化委托callBack cb=new callback(mc.Plus);//调用委托int ret=cb(6,7);Console.WriteLine(ret);}}
7.1.2.匿名方法
语法:委托的定义和调用都没有变化 委托类型 委托实例=delegate([参数列表]) {代码块 };//分号不能少
匿名方法优缺点:
-
减少了系统开销,方法仅在由委托使用时才定义。
-
减低代码的复杂性。
-
委托代码块很多容易混淆代码,降低程序可读性。
-
不能用跳转语句跳到外部,外部也跳不过来。
-
不能访问外部使用的ref和out修饰的参数,但能访问在外部声明的其他变量。
7.2.Lambda表达式
语法: (参数类型 参数)=>表达式
7.3.事件
-
定义事件 [访问修饰符] event 委托名 事件名;
C#中事件借助委托来实现,使用委托调用事件订阅者的某个方法
-
订阅事件 事件名 +=new 委托(事件处理方法名);
-
引发事件 事件名[参数列表] 如果没有对象定义该事件,则会引发异常错误
7.3.1.自定义事件完整实例
using System; namespace EventTest {//Teacher 是教师类public class Teacher{//定义一个委托和事件public delegate void ListEventHandler();public event ListEventHandler ListenerEvent;//定义引发事件的方法public void BeginCalss(){Console.WriterLine("教师宣布开始上课了");if(ListenerEvent!=null){ListeneEvent()//引发事件}}}//Student学生类public class Student{private string stuName;//学生姓名//构造方法public Student(string name){this.stuName=name;}public void Listener(){Console.WriteLine("学生:"+stuName+"认真听课");}public void Record(){Console.WriteLine("学生:"+stuName+"摸鱼");}public void Sleep(){Console.WriteLine("学生:"+stuName+"睡觉");}}class Program{static void Main(string []args){Teacher t=new Teacher();Student stu1=new Student("晓星尘");Student stu2=new Student("陈情令");Student stu3=new Student("魏无羡");Student stu4=new Student("蓝忘机");//四名同学都定义了该事件t.ListenerEvent+=new Teacher.ListenerEventHandler(stu1.Listener);t.ListenerEvent+=new Teacher.ListenerEventHandler(stu1.sleep);t.ListenerEvent+=new Teacher.ListenerEventHandler(stu1.Record);t.ListenerEvent+=new Teacher.ListenerEventHandler(stu1.Record);t.BeginClass();//触发事件}}}
7.5含参数事件完整实例
[访问修饰符] 返回值类型 事件处理方法名(odject source ,EventArgs e)
8.继承与多态
8.1.继承
子类又叫派生类,父类又叫基类。
语法: class 类名:父类类名 {} //继承某个类,就拥有那个类的特征(成员变量)和行为(成员方法).
语法总结:
-
继承的单根性。一个类,只能有一个父类,但是一个父类可以有多个子类。
-
继承的传递性。
-
子类可以从父类继承变量成员,但是不能继承构造方法。
8.1.1.继承中的构造方法
-
派生类没有定义构造方法,编译器会提供一个默认的无参构造方法,子类的构造方法在执行的时,会先自动调有父类的无参构造方法。以初始化继承来的字段。
-
在创建派生类对象时,必须初始化该对象的添加的字段和继承部分。通过base关键字,派生类的构造方法可以显式调用基类的构造方法。
//首先会执行父类的带参构造方法,然后再执行子类 语法:子类构造方法:base(参数变量名)
8.1.2.base关键字和protected访问修饰符
private修饰符修饰父类成员,子类不能用base访问,父类的成员如果用private 修饰,其他任何类都无法访问该成员,子类也一样不性。
public修饰所有类都可以访问。所有C#提供了另外一个访问修饰符——protected。
class Person {protected string _name;protected int _age;public Person(string name,int age){this._name=name;this._age=age} } class Student:Person {protected string _learn;public Student(string learn,string name,int age):base(name,age){this._learn=learn; } }
8.1.3.Odject类
所有的类都派生自一个名为Object类。。。。
8.2.封装类
一个类如果不希望继承,可以把这定义为封装类,封装类需要使用sealed关键字。
sealed class odjl {}
封装类不能被继承
-
把类定义为封装时,最有可能的情形:因为商业原因把类标记为封装类,以防止第三方以违反注册协议的方法扩展类。把类标记为封装类会严限制它的使用。
-
sealed修饰的类的成员不能被protected修饰。
8.3.多态
多态类:1.之前学的重载,2.重写 .......
多态的一个重要的特性——重写, 重写需要两个关键字(virtual和override)
重写父类方法两个步骤:
1.在父类中把某个方法定义为虚方法,使用virtual关键字。
2.在子类中使用override关键字重写父类的虚方法。
class Mybase {public virtual void read(){Console.WriteLine("父类");} } class Myday:MyBase {public override void read(){Console.WriteLine("子类");} } //父类的对象可以指向子类实例,反之则不行 MyBase mb=new Myday();
9.抽象类和接口
9.1.抽象类
一个类专用于基类来派生出其他类,则把这个类定义为抽象类。
抽象类不能被实例化,它是派生类的继承。
抽象类语法定义: [访问修饰符] abstract class 类名 {代码 }
-
抽象类包含零个或多个抽象方法,也可以包含零个或多个非抽象方法。
-
抽象类可以有构造方法,但该构造方法不能直接被调用而只能由派生类的构造方法调用。
-
定义抽象方法的目的:指定派生类必须实现这个功能(就是为方法添加代码)。
-
抽象方法只能派生类才能真正实现 ,定义抽象方法使用abstract关键字。
-
抽象方法只能只指名方法的返回值类型,方法名称及参数,而不能提供方法的实现。
-
抽象方法只有方法头的定义,没有方法体。
-
抽象方法只能定义在抽象类中,而且抽象类不能被实例化。
-
派生自抽象类的类需要基类的所有抽象方法才能被实例化,否则该派生类也是抽象类。(不实例好像会报错)
-
用override实现基类的抽象方法时,方法的签名必须与基类的方法相同。
abstract class Animal {protected float weight;//体重protected Animal(float wei){this.weight=wei;}//描述动物奔跑的抽象方法public abstract void Run()public void Eat(){Console.WriteLine('动物都进食')} } //派生的袋鼠类 class Kangaroo:Animal {//构造方法public Kangaroo(float wei):base(wei){}//实现基类的抽象方法public override void Run(){Console.WriteLine("袋鼠")}public void ShowInfo(){Console.WriteLine("袋鼠体重")} }static void Main(string[] args) {Kangaroo knew Kangaroo(80.5f);k.Eat();//调用基类方法k.Run();//调用基类抽象方法k.ShowInfo();//调用子类方法 }
9.2接口
接口不能包含任何实现了的方法。
一个类对接口的实现与派生类实现基类方法的重写一样,只是接口中的所有方法都必须在派生类中实现。
接口的作用:指明实现特定接口的类必须实现该接口列出的所有成员,它指明了一个类必须具有哪些功能。
[修饰符] interface 接口名 {接口主体 }
C#中定义一个接口时,需要注意以下几点:
-
接口中只能声明方法,属性,索引器和事件。
-
接口不能声明字段,构造方法,常量和委托。
-
接口的成员默认是public 的,如果明确指定成员的访问级别会报编译错误。
-
接口中所有的方法,属性和索引器都必须没有实现。
-
C#的接口以大写字母I开头。
使用接口:
-
类实现接口与继承一样,需要使用冒号运算符。
-
与抽象类不同的是,类实现了接口中的方法而不是重写,所以实现接口的方法时不需要override关键字.
-
一个类即可以派生自另一个类,还可以同时实现接口。(接口的实现类也可以有自己的方法)
注意:一个类继承基类同时又实现接口时,基类名要写在接口名的前面。
C#不允许多重继承,也就是一个类不能同时派生自多个类,但是C#允许多重接口实现,也就是一个类可以有多个接口。
实现多个接口只需要在定义类时在后面添加一个逗号和接口名。
9.2.1.is 和as 关键字
知道一个实例是不是另一个类型,可以使用is关键字。
static void Main(string[] args) {Circle c=new Circle();c.Print();if(c is IDraw){IDraw d=(IDram)c//强制转换} }
is关键字后面可以接类,接口,基本数据类型,接口和枚举等。
关键字as也可以是测试一个实例是否是某个类型。
as关键字在测试的同时把实例转换为另一种类型,如果转换不成功,则返回null。
接口绑定:
-
接口绑定就是把不同的接口合在一起变成一个新的接口。
-
任何的接口类型都必须实现所有的方法才能实例化。
-
接口隐藏了实现的具体细。用户不必关心代码是如何实现的,只需要调用相应的功能就可以了。
######
10.常用类
10.1.Math类
Math是一个静态类,它通过数字函数提供常数和静态方法。
方法 | 说明 |
---|---|
Abs() | 已重载,返回指定数字的绝对值 |
Acos() | 返回余弦值为数字的角度 |
Asin() | 返回正弦值为数字的角度 |
Atan() | 返回正切值为数字的角度 |
Atan2() | 返回正切值为两个数字的商的角度 |
Ceiling() | 已重载,返回大于或等于指定数字的最小整数 |
Cos() | 返回指定角度的余弦值 |
Cosh() | 返回指定的双曲线余弦值 |
DivRem() | 已重载。计算两个数字的商,并在输出参数中返回余数 |
Exp() | 返回e的指定次幂 |
Floor() | 已重载。返回小于或等于指定数字的最大整数 |
Log() | 已重载,返回指定数字的对数 |
Log10() | 返回指定数字以10为底的对数 |
Max() | 已重载。返回两个指定数字中较大的一个 |
Min() | 已重载。返回两个指定数字中较小的一个 |
Pow() | 返回指定数字得指定次幂 |
Round() | 已重载。将值舍入到最接近的整数或指定的小数位数 |
Sign() | 已重载。返回表示数字符号的值 |
Sin() | 返回指定角度的正弦值 |
Sinh() | 返回指定角度的双曲线正弦值 |
Sqrt() | 返回指定数字的平方根 |
Tan() | 返回指定角度的正切值 |
Tanh() | 返回指定角度的双曲线正切值 |
Truncate() | 已重载。计算一个数字的整数部分 |
10.2.Random类
Random类表示伪随机数生成器
方法 | 说明 |
---|---|
Next() | 已重载。返回随机数 |
NextBytes() | 用随机数填充指定字节数组的元素 |
NextDouble() | 返回一个介于0.0和1.0之间的随机数 |
Sample() | 返回一个介于0.0和1.0之间的随机数 |
ToString() | 返回表示当前Odject的String(继承自Odject) |
它的构造方法有两种:
一个是直接函数New Random(),根据触发那一刻的系统时间作为种子,来产生随机数字
另一个是New Random(Int32)函数,可以自己设定触发的种子,一般都用UnCheck((Int)DateTime.Now.Ticks)作为参数种子。
1.如果计算机运行速度很快,触发Random函数间隔时间很短,就有可能产生一样的随机数,因为伪随机数的数字在Random的内部产生机制中还是有一定规律的,并非是真正意义上的完全随机。
2.所谓的随机数发生器都是通过一定的算法对事物先选定的随机种子做复杂的运算,用产生的结果来近似模拟完全随机数,这种随机数被称为伪随机数。
3.使用两种方式初始化一个随机数发生器:
(1).不指定随机种子,系统自动选取当前时间作为随机种子。
Random ran=new Random();
(2).可以指定一个int型参数作为随机种子。
int iSeed=10; Random ro=new Random(10); long tick =DataTime.Now.Ticks;//DataTime.Now.Ticks 的值表示自0001年1月1日午夜12:00:00以来所经历的以100ns为时间间隔的间隔数 Random ran=new Random((int)(tick & 0xffffffffL) | (int)(tick>>32)); //这样可以确保99%不一样
指定随机数的上下限:
(1).不指定上下限的使用如下:
int iResukt=ro.Next();
(2).下面的代码指定返回小于100的随机数:
int iResult=ro.Next(100);
(3).下面的代码指定返回值必须在50~100的范围之间。
int iResult=ro.Next(50,100);
10.3.DataTime结构
DataTime是一个结构体。
构造函数如下:
函数名称 | 说明 |
---|---|
DataTime() | 已重载。初始化DateTime结构的新实例 |
属性如下:
属性名 | 说明 |
---|---|
Now | 静态属性,返回当前的日期和时间 |
Today | 静态属性,返回当前日期 |
方法如下:
方法名称 | 说明 |
---|---|
Add() | 将指定的TimeSpan的值加到此实现的值上 |
AddDays() | 将指定的天数加到此实例的值上 |
AddHours() | 将指定的小时数加到此实例的值上 |
AddMillisecondes() | 将指定的毫秒数加到此实例的值上 |
AddMinutes() | 将指定的分钟数加到此实例的值上 |
AddMonths() | 将指定的月份数加到此实例的值上 |
AddSeconds() | 将指定的秒数加到此实例的值上 |
AddTicks() | 将指定的刻度数加到此实例的值上 |
AddYears() | 将指定的年份数加到此实例的值上 |
DaysInMoth() | 返回指定年和月中的天数 |
GetDateTimeFormats() | 已重载。将此实例的值转换为标准 DateTime格式说明字符支持的所有字符串表示形式 |
IsLeapYear() | 返回指定的月份是否为闰年的指标 |
Parse() | 已重载。将日期和时间的指定字符串表示形式转换为其等效的DateTime |
ParseExact() | 已重载。将日期和时间的指定字符串表示形式转换为其等效的 DateTime 。字符串表示形式的格式必须与指定的格式完全匹配。 |
SpecifyKind() | 创建新的 DateTime 对象,该对象表示与指定的DateTime 相同的时间,但是根据指定的DateTimeKind值的指示。指定为本地时间或协调通用时间(UTC),或者两者皆否 |
subtract() | 已重载。从此实例中减去指定的时间或持续时间 |
ToBinary() | 将当前DateTime 对象序列化为一个64位的二进制值,该值随后可用于重新创建DateTime对象 |
ToLocalTime() | 将当前DateTime 对象转换为本地时间 |
ToLongDateString() | 将当前DateTime对象的值转换为其等效的长日期字符串表示形式 |
ToLongTimeString() | 将当前DateTime对象的值转换为其等效的长时间字符串表示形式 |
ToShortDateString() | 将当前DateTime对象的值转换为其等效的短日期字符串表示形式 |
ToShortTimeString() | 将当前DateTime对象的值转换为其等效的短时间字符串表示形式 |
ToString() | 已重载。将当前DateTime对象的值转换为其等效的字符串表示形式 |
ToUniversalTime() | 将当前DateTime对象的值转换为协调通用时间(UTC) |
TryParse() | 已重载。将日期和时间的指定字符串表示形式转换等效的DateTime |
TryParseExact() | 已重载。将日期和时间的指定字符串表示形式转换为其等效的DateTime。字符串表示形式的格式必须与指定的格式完全匹配 |
列如下面代码:
using System; namaspace Demo {class Program{static void Main(string[] args){//获取此刻时间信息存于now变量DateTime now =DateTime.Now;//获取今天星期几,其会返回枚举类型值(sunday,monday...),对应从星期天到星期一DayOfWeek now_dayOfWeek=now.DayOfWeek;//将今天星期几转换成intint now_dayOfWeek_ToInt=Convert.ToInt16(now_dayOfWeek);//获取本第一天(也就是星期天)的日期//因为一周是从0开始,如果今天是星期n,那么星期天是n天前DateTime fristDayOfThisWeek=DateTime.Now.AddDays(-now_dayofWeek);//获取本周最后一天(也就是星期六)的日期//因为一周是从0开始,以6结束,如果今天是星期n,那么星期6-n天后DateTime endDayOfThisWeek=DateTime.Now.AddDays(6-now_dayofWeek_ToInt);//定义一个数组,和一周的7天对应string[] Day=new string[]{"星期日","星期一","星期二","星期三","星期四","星期五","星期六"};//输出今天星期几Console.WriteLine(Day[now_dayofWeek_ToInt]);//输出本周第一天的日期Cnnsole.WriteLine(firstDayOfThisWeek.ToshortDateString());//输出本周最后一天的日期Cnnsole.WriteLine(endDayOfThisWeek.ToshortDateString());}} }
10.4.System.String类
System.String类是专门用来储存字符串的类
实例方法 | 作用 |
---|---|
CompareTo() | 比较字符串的内容是否相等,相等返回0 |
Contains() | 判断字符串中是否包含另一个小的字符串 |
CopyTo() | 把特定数量的字符从字符串中特定的下标开始复制到数组中 |
EndsWith() | 判断字符串是否以指定的字符串结尾 |
IndexOf() | 返回某个字符或某个小的字符串在字符串中出现的索引,未出现返回-1 |
IndexOfAny() | 返回字符数组中任意一个字符最先出现的索引,未出现返回-1 |
Insert() | 在指定的索引处插入一个新的字符串 |
LastIndexOf() | 返回某个字符或某个小的字符串在字符串中出现的索引,从后往前计算 |
LastIndexOfAny() | 返回字符数组中任意一个字符最先出现的索引,从后往前计算 |
Remove() | 从指定索引处开始删除指定数量的字符,返回删除后的字符串 |
Replace() | 替换字符串中指定的字符或字符串 |
Split() | 将字符串分隔为小的字符串数组 |
StartsWith() | 判断是否以指定的字符串开始 |
SubString() | 提取一个小的字符串 |
ToCharArray() | 得到字符串对应的字符数组 |
ToLower() | 得到字符串对应的全部小写形式 |
Trim() | 删除字符串两端的指定字符 |
System.String除了具有很多实例方法外还具有一个代表空字符串的静态属性Enpty和一些静态方法,常用静态方法如下:
静态方法 | 作用 |
---|---|
Concat() | 连接多个字符串 |
Format() | 格式化字符串 |
join() | 用指定的分隔符连接多个字符串 |
IsNullOrEmpty() | 测试字符串引用是否为null或string.Empty |
使用String.Format()方法代码:
string name="张扬"; int age=18, string str=string.Format("我叫{0},今年{1}岁",name,age);
C#开始,C#引入内插字符串的语法。使用$“字符串{变量1}字符串{变量2}”。
10.5.StringBuilder类
因为string类实际上是一个不可改变的数据类型,一旦对该类型的对象进行初始化,该字符串对象就不能改变了,任何修改实际上是创建一个新的字符串并引用变量指向新创建的字符串对象,这时旧的字符串对象就被撤销了引用,即不会再有变量引用它,下一次垃圾收集器清理未使用的对象时删除它。如果过多插入,删除或替换就会明显降低性能。就解决这问题,Microsoft提供了System.Text.StringBuilder类。
StringBuilder类不具备String类的强大功能,在StringBulder上可以进行的操作仅限于添加,删除和替换字符串中文本,但是它的效率很高。
构造方法 | 说明 |
---|---|
StringBuilder() | 无参数,构造一个空字符串 |
StringBuilder(string value) | 初始字符串 |
StringBuilder(string value,int capacity) | 初始字符串和初始容量 |
StringBuilder类的主要方法如下:
方法 | 作用 |
---|---|
Append() | 给当前字符串追加一个字符串或其他对象 |
Insert() | 在当前字符串中插入一个子字符串 |
Remove() | 当前字符串中删除字符 |
Replace() | 将字符串中的一部分字符替换为其他的字符 |
ToString() | 把当前字符串转换为string对象 |
不能把StringBuilder直接转换为String,显示转换或隐式转换都不行。如果要把Stringbuilder的内容输出为String,唯一的办法就是使用ToString()方法.
10.6.正则表达式
正则表达式可以看作是一种特定功能的小型编程语言:在大的字符串表达式中定位一个子字符串。.NET架构专门提供了System.Text.ReularExpressions这个命名空间来支持正则表达式。
10.6.1.System.Text.RegularExpressions命名空间
在.NET中实现正则表达式的关键字System.Text.RegularExpressions命名空间,包含下面8个类:
Regex——包含正则表达式,以及使用正则表达式的各种方法。
MatchCollection——包含一个正则表达式找到的所有匹配项。
Macth——包含一次匹配中所有匹配的文本。
GroupConllection——包含一次匹配中的所有分组。
Group——包含一个分组集合中一个组的细节。
CaptureConllection——包含一个组的所有Capture对象。
Capture——返回组内一次捕获所匹配的字符串。
10.6.2.Regex类
标志 | 描述 |
---|---|
IgnoreCase | 忽然大小写,默认是区分大小写的 |
None | 不设定标志,这是默认选项 |
MultiLine | 指定了^和$可以匹配的开头和结尾,以及字符串的开头和结尾。也就是说,使用换行符分隔,在每一行都能得到不同的匹配。但“.”仍然不匹配换行符。 |
默认状态下所有的标志都没有设定。可以使用RegexOptions枚举来设定标志,如RegexOptions.IgnoreCase等。
Regex类有如下两个主要的构造函数:
1.Regex(string pattern);
2.Regex(string pattern ,RegexOptions options);
第一个参数是要匹配的正则表达式,看下面列子:
using System; using System.Text; using System.Text.RagularExpressions; namespace MyTest {class Program{Regex myReEx=new Ragex("ABC");Console.WriteLine(myReEx.IsMatch("there is much ABC"));} } //输出结果是true,因为there is much ABC中包含ABC
如果要匹配ABC或abc或Abc等任意的大小组合,则可以选第二种构造方法:
Regex myReEx=new Regex("ABC",RagexOptions.IgnoreCase); Console.WriteLine(myReEx.IsMatch("there is much Abc")); //输出结果是true
Regex类的IsMatch()方法可以测试字符串是否匹配某个正则表达式模式。
IsMatch()还有两个静态的重载方法,使用这些静态的重载方法可以不用显式创建一个Regex对象。
-
public bool IsMatch(string input);
-
public bool IsMatch(string input,int startat);
-
public static bool IsMatch(string input,string pattern);
-
public static bool IsMatch(string input,string pattern,RegexOptions options);
后两个重载是静态的,前面两个用于对Regex对象进行操作。除了RegexOptions枚举外,后面两个的第一个参数是待测的字符串,第二个是匹配模式。
string input="there is much ABC,here is no ABC" Regex.IsMatch(input,"ABC",RegexOptions.IgnoreCase);
Regex类的Replace()方法可以指定的字符串去代替一个模式,下面是该方法的两个静态重载
-
public static string Regex.Replace(string input,string pattern ,string replacement);
-
public static string Regex.Replace(string input,string pattern ,string replacement ,RegexOptions options);
string input ="there is much ABC,here is no ABC"; string inputout=Regex.Replace(input,"ABC","egg"); //结果是:there is much egg,here is no egg
如果使用非静态方法,可以指定替换的最大次数和从哪个位置开始替换。
-
public string Replace(string input,string replacement,int count,int startat)
string input="123,456,123,123,678,123,666" Regex myRe=new Regex("123"); string inputout=myRe.replace(input,"xxx",2,4); //结果是:123,456,xxx,xxx,678,123,666
Regex类的Split()在每次发现匹配的位置拆分字符串时,该方法返回一个字符串数组。下面它的两个重载。
public static string[] Split(string input,string pattern);
public static string[] Split(string input ,string pattern,RegexOptions options);
string input ="there,is much ABC,here, is no ABC"; string[] inputout=Regex.Split(input,","); foreach(string s in inputout) {Console.WriteLine(s); } //结果: //there //is much ABC //here //is no ABC
10.6.3.Match和MatchConllection类
利用正则表达式匹配的时往往不止1次,如果想要知道细节需要用到Match类和MatchConllection类,Match表示一次成功的匹配,MatchConllectioon类是一个Match对象的集合。可以是使用Regex类的Match()方法来得到一个Match方法来得到一个Match对象或者用Matches()方法来得到一个MatchConllection对象。
下面代码找到所有以se开头的三个字母:
string input ="a sailor went to sea to set,"+"to see what be could sec"; MatchConllection mc=Regex.Matches(input,@"se\w"); Console.WriteLine("共找到{0}个匹配",mc.Count;foreach(Match s in mc){Console.WriteLine("在索引{0},处发现:{1}",s.Index,s.Value);}//结果:共找到4个匹配在索引17,处发现:sea 在索引24,处发现:set在索引31,处发现:see在索引48,处发现:sec
10.6.4.模糊匹配
字符 | 描述 |
---|---|
\ | 将下一个字符标记为一个特殊字符,或一个原文字符,或一个向后引用,或一个八进制转义符。列如,‘n’匹配‘n’。'\n'匹配一个换行符。序列‘\\’匹配”\“ 而‘\(’ 则匹配”(“ |
^ | 匹配输入字符串的开始位置 |
$ | 匹配输入字符串的结束位置 |
* | 匹配前面的子表达式零次或多次。列如,'zo*'能匹配”z“以及”zoo“。星号等价于{0,} |
+ | 匹配前面的子表达式一个或多次。列如,'zo+'能匹配"zo"以及"zoo"。但不能匹配"z"。+等价于{1,} |
? | 匹配前面的子表达式零次或一次。列如,”do(es)?“ 可以匹配 ”do“或”does“中的”do" 。?等价于{0,1} |
{n} | n是一个非负整数。匹配确定的n次。列如,’o{2}'不能匹配“Bob”中‘o’,但是能匹配“food”中的两个o |
{n,} | n是一个非负整数。至少匹配n次。列如,'o{2,}'不能匹配"Bob"中的’o‘,但能匹配“foooood"中的所有o。’o{0,}则等价于‘o*’ |
{n,m} | m和n 均为非负整数,其中n<=m,最少匹配n次且最多匹配m次。列如,”o{1,3}"将匹配“fooooood"中的前三个o,‘o{0,1}’等价于'o?'。注意在逗号和两个数之间不能有空格 |
? | 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m}后面时,匹配模式是非贪婪的,非贪婪模式尽可能少地匹配所搜索的字符串,而默认的贪婪模式则尽可能多地匹配所搜索的字符串。列如,对于字符串”oooo",“o+?"将匹配单个”o",而'o+'将匹配所有’o' |
x|y | 匹配x或y。列如,‘{z|food}'能匹配”z“或”food"。’(z|f)ood',则匹配”zood"或“food" |
[xyz] | 字符集合。匹配所包含的任意一个字符。列如,”[abc]"可以匹配“plain"中的”a" |
[^xyz] | 负值字符集合,匹配未包含的任意字符串。列如,”[abc]"可以匹配“plain"中的”p" |
[a-z] | 字符范围,匹配指定范围的任意字符。列如,“[a-z]"可以匹配’a'~'z'范围内的任意小写字母字符 |
[^a-z] | 负值字符范围,匹配指定范围的任意字符。列如,“[a-z]"可以匹配’a'~'z'范围内的任意小写字母字符 |
\d | 匹配一个数字字符,等价于[0-9] |
\D | 匹配一个非数字字符。等价于[^0-9] |
\s | 匹配任何空白字符,包含空格,制表符,转页符 |
\S | 匹配任何非空白字符 |
\w | 匹配包括下画线的任何单词字符。等价于‘[A-Za-z0-9]’ |
\W | 匹配任何非单词字符。等价于‘[A-Za-z0-9]' |
下面用上面的模糊匹配来检验一个字符串的合法的日期格式。如果1984-05-22就是合法的,2100-14-7就是不合法的,具体要求
-
日期按顺序由年,月,日三部分组成,每部分之间用”-“隔开
-
年份必须是4位数字,必须是19或20开头
-
月份必须是两位数字,且必须写成01~09。
-
日期必须是两位数字,且必须写成01~09。
满足1条件写成\d{4}-\d{2}-\d{2}就可以了
满足2条件:可以写成([19|20]\d{2})
满足3条件:一是01~09月 可以写成(0[0-9]),二是10~12月可以写成(1[0-2]),合起来就是(0[1-9] | 1[0-2])
满足4条件:一是01~09日 可以写成(0[0-9]),二是10~29日可以写成([12] [0-9]),三是30~31日可以写成(3[0-1]),合起来就是(0[1-9] | [12] [0-9] | 3[0-1])
全部条件合起来就是:([19|20]\d{2}) - (0[1-9]|1[0-2]) - (0[1-9] | [12] [0-9] | 3[0-1])
但是是”只能包含“而不是”只要包含“所有要把它写成 ^([19|20]\d{2}) - (0[1-9]|1[0-2]) - (0[1-9] | [12] [0-9] | 3[0-1])$,如果没有^和$匹配可能出212002-01-01000也符合要求。
11.集合和泛型
11.1.System.Conlllection空间
下列表列出了System.Conllection空间下常用的类,接口和结构
类 | 描述 |
---|---|
ArrayList | 提供适用于大多数用途的一般集合功能,它允许动态添加或删除 |
HashTable | 存储键值对,这些键值对是根据键值编码来安排的 |
接口 | 描述 |
ICollection | 为所有集合定义大小,枚举器和同步方法 |
IEnumerator | 对整个集合的简单循环和枚举 |
Ilist | 通过索引进行单独访问的对象的集合 |
结构 | 描述 |
DictinaryEntry | 定义可以设置或检索的字典键值对 |
11.1.1.ArrayList类
ArrayList类实际上是集合Array类的优化版,区别在于ArrayList提供了大部分集合类具有而Array类没有的特色。如下
-
Array的容量或元素个数是固定的,而ArrayList的容量可以根据需要动态扩展。通过设置ArrayList.Capcity的值可以执行重新分配内存和复制元素操作。
-
可以通过ArrayList提供的方法在某个时间追加,插入或移出一组元素,而在Array中一次只能对一个元素进行操作。但是Array具有Array List所没有的灵魂性。列如:
1.Array 的下标是可以设置的,而ArrayList的下标始终是0
2.Array可以是多维的,而ArrayList始终是一维的。
ArrayList类支持Array类的大多数方法,ArrayList的常用属性和方法:
属性 | 描述 |
---|---|
Capacity | 指定数组列表可以包含的元素个数,也就是容量 |
Count | 数组列表中元素的实际个数 |
方法 | 描述 |
Add() | 在数组列表中尾部追加元素 |
Contains() | 检测数组列表是否包含指定元素 |
Insert() | 在指定位置插入一个元素 |
Remove() | 从数组列表中移出第一个出现的给定元素 |
RemoveAt() | 移出数组列表中指定索引处的元素 |
TrimToSize() | 将数组列表容量缩小为元素个数 |
ArrayList的容量通常大于或等于Count值,如果添加元素时Count值大于容量,则容量自动增加一倍
ArrayList 对象名称=new ArrayList(初始容量); Console.ReadLine()//用户输入语句
11.1.2.HashTable类
用户可以将联系人的姓名作为键来引用,而联系人号码作为值来引用
属性 | 描述 |
---|---|
Count | 该属性用于获取哈希表中键值对的数量 |
方法 | 描述 |
Add() | 将一个键值对添加到哈希表中 |
ContainsKey() | 测试是否已经存在 |
Remove() | 根据键将对应的键值对从哈希表移出 |
在哈希表中添加重复的键会发生ArgumentException异常,修改某个键值对应的值可以:
哈希表名[键]=值//初始化HashTableHashTable hs=new HashTable();
11.2.泛型集合
-
ArrayList和HachTable两种集合类,但这样的集合是没有类型化的,类型不安全,很容易出现类型访问错误。
-
只要是继承自System.Odject的任何对象都可以存储在ArrayList中,而实际应用中往往向集合中存放某种特定类型的数据。所有在编译不同类型到Array List都不会有语法错误但是运行时会出现编译错误。因为所有的类型的对象都时继承自System.Odject类型
-
泛型集合是一种要明确限定放入集合中数据类型的集合,使用泛型集合时编译器会在编译期间检查要放入集合的对象的数据类型,如果发现不是被限定的类型就会报错,这样就可以避免发生运行错误,这是泛型集合的一大优点——类型安全。泛型集合的另一个优点是可以提高性能,由于明确了数据类型,所以在存储数据时不会发生类型转换,特别时存取值类型时不会发生装箱和拆箱操作。
11.2.1.System.Conllections.Generic命名空间
泛型集合类属于System.Collections.Generic命名空间
泛型集合类 | 描述 |
---|---|
List< T > | 一般用于替代ArrayList类,与ArrayList很相似 |
Dictionary<TKey,TValue> | 存储键值对的集合泛型类 |
SortedList<TKey,TValue> | 类似于Dictionary<TKey,TValue>,但按键自动排列 |
LinkedList< T > | 双向链表 |
Queue< T > | 先进线出队列类 |
Stack< T > | 后进先出的堆栈类 |
c#中没有ArrayList< T >和HashTable<TKey,TValue>这两个泛型集合类,而且用List< T >和Dictionary<TKey,TValue>类代替。
11.2.2List< T >类
语法: 泛型集合类< 数据类型>实例名=new 泛型集合类<数据类型>(); 实例: using System; using System.Collections.Generic; namespace GenericCllectionsDemo {class Program{static void Main(string[] args){Student s1=new Student(1,"小小");Student s2=new Student(2,"的");Student s3=new Student(3,"小");Student s4=new Student(4,"dd的");//无法通过编译,无法将string转换为GenericCollecCollectionsDemo.Student// Student s1=new Student(1,"小小");//List<Student> student=new List<Student>{s1,s2,s3,s4,"1"}List<Student> students=new List<Student>{s1,s2,s3,s4}foreach(var s in students){Console.WriteLine(s)}}}public class Student {public Student(int id,string name){Id=id;Name=name;}public int Id{get;set;}public string Name{get;set;}public override string ToString(){return "姓名:"+Name+",学号:"+Id} }}
List< T> 类的FindAll()方法检索与指定谓词(返回true或false的方法)所定义的条件相匹配的所有元素。如果找到,则返回一个包含与指定谓词所定义的条件相匹配的所有元素构成的List< T>,否则为一个空List< T>。
var _student=students.FindAll(m=m.Id<3) _student.forEach(s=>{Console.WriteLine(s)}) 结果:姓名:小小,学号:1姓名:的,学号:2
11.2.3Dictionary<TKey,TValue>类
语法:Dictionary<数据类型,数据类型> 实例名=new Dictionary<数据类型,数据类型>(); 实列: using System; using System.Collections.Generic; namespace DictionaryDemo {class Progam{static void Main(string[] args){Dictionary<string,Student> students=new Dictionary<string,Student>();Student stu1=new Student(1,"小小");Student stu2=new Student(2,"的");Student stu3=new Student(3,"小");Student stu4=new Student(4,"dd的");student.Add(stu1.Name,stu1);student.Add(stu2.Name,stu3);student.Add(stu3.Name,stu4);student.Add(stu4.Name,stu5);forEach(string name in students.Keys){//students[Key]指向的是students[name]即一个Student对象Console.WriteLine(students[name])}}}public class Student {public Student(int id,string name){Id=id;Name=name;}public int Id{get;set;}public string Name{get;set;}public override string ToString(){return "姓名:"+Name+",学号:"+Id} }}
11.3.扩展方法
class Program {static void Main(string[] args){string str="厚福"Console.WriteLine(str.ReturnStr()); } }public static class MyExtensions {//1.扩展方法必须在静态类中//2.方法本身必须是静态方法//3.方法的第一个参数的类型必须是要扩展的类型,且此类型前面必须有一个this关键字public static string ReturnStr(this string str){retrun str+":扩展方法"} }
11.3.1.泛型集合常用扩展方法
.NET 框架在System.Core程序集里的System.Linq命名空间下为泛型集合创建了非常丰富的扩展方法。
常用扩展方法 | 方法功能 |
---|---|
FristOrDefault | 返回泛型集合中的第一个元素 |
All | 该方法需要传入一个谓词委托,检验泛型集合中是否所有元素都满足条件,全部满足返回true,否则返回false |
Any | 检查泛型集合中是否有任何一个元素满足条件,可指定条件判断方法,存在一个及以上满足返回true,否则返回false |
Count | 返回泛型集合中满足指定条件的元素的数量,可指定条件判断方法 |
ToArray | 将泛型集合转换成一个相对应的数组 |
Where | 根据指定对泛型集合元素进行过滤,返回满足条件的元素集合 |
11.3.2.d.泛型集合扩展方法的实现机制
where()方法的定义如下:
public static IEnumerable<TSource> where (this IEnumerable<TSource,bool>predicate);
-
实现这个方法的类Enumerable是个静态类,该方法是对IEnumerable< TSource >这个接口的扩展。
-
ArrayList,HashTable都没有直接或间接实现IEnumerable< T >接口,这就解决了为什么List< T>和Dictionary<TKey,TValue>具备前述扩展方法,而普通集合不具备,即需要直接或者间接实现IEnumerable< T>接口。
11.4.IComparable接口实现排序
-
泛型集合对象的Sort()方法,对字符串类型的元素是按照首字母的升序进行排序。Sort()方法默认就是以这样的排列方式进行排序。无法比较集合中的元素大小。
-
在.NET框架类库中提供了一个IComparable接口,该接口定义了名为CompareTo(object odj)的方法,该方法旨在告诉系统怎么对当前的对象进行比较。任何想进行排序的对象都要实现该方法。
//student类实现IComparable接口 class Student:IComparable {public int stuid;public int age;public string name;public Student(int Stuid,int Age,string Name){this.stuid=Stuid;this.age=Age;this.name=Name;}//实现IComparable接口的CompareTo()方法public int CompareTo(object odj){if(!(odj is Student)){throw new Exception("比较对象不是Student对象")}Student other=odj as Student;return this.name.CompareTo(other.name);//接受参数的name成员进行比较,并返回结果。} } class Program {static void Main(string[] args){List<Student> list=new List<Student>();Student stu1=new Student(1,18,"张三");Student stu2=new Student(2,20,"李四");Student stu3=new Student(3,17,"王五");list.Add(stu1);list.Add(stu2);list.Add(stu3);Console.Write("排序前顺序");foreach(Student stu in list){Console.WriteLine(stu.name+" "+stu.age.ToString())}list.Sort();Console.WriteLine("排序后顺序");foreach(Student stu in list){Console.WriteLine(stu.name+" "+stu.age.ToString());}} } 结果:排序前顺序张三 18李四 20王五 17排序后顺序李四 20王五 17张三 18
11.5.泛型接口
11.5.1.IComparable< T>接口
实现CompareTo(odject odj)方法的代码比较复杂,必须判断参数odj是否是student类的对象,如果不是,还会抛出异常,显得非常不安全。
同IComparable接口对应的泛型接口IComparable< T>,泛型接口数据类型有着严格的控制。
//Student类实现IComparable<T>接口 class Student:IComparable<Student> {public int stuid;public int age;public string name;public Student(int Stuid ,int Age,string Name){this.stuid=Stuid;this.age=Age;this.name=Name;}//实现IComparable<T>接口,参数必须是Student类型public int CompareTo(Student other){//不要进行类型转换//不用判断参数other是否是Student类型,return this.name.CompareTo(other.name);}}
11.5.2.IComparer< T>接口
-
Sort()方法的排序方式是写死的,如果想按照其他方式进行,就必须重新编写Comparer()方法的代码。
-
IComparer< T >接口,有一个未实现的方法int Compare(T x,T y),方法有两个参数,表示要进行比较的两个对象。它还有一个返回值,返回值如果大于0,则x>y,如果小于0,则x<y;如果等于0,则x=y。
public class Student {public int stuid;public int age;public string name;public Student(int Stuid,int Age,string Name){this.stuid=Stuid;this.age=Age;this.name=Name;} } //按照姓名首字母升升序类 public class NameSort:IComparer<Student> {//按照姓名首字母升序排序public int Compare(Student x,Student y){return x.name.CompareTo(y.name);} } //按照年龄升序类 public class NameSort:IComparer<Student> {//按照年龄升序排序public int Compare(Student x,Student y){return x.age.CompareTo(y.age);} }static void Main(string[] args) {Student stu1=new Student(1,18,"张三");Student stu2=new Student(2,20,"李四");Student stu3=new Student(3,17,"王五");List<Student> list=new List<Student>(){stu1,stu2,stu3};Console.Write("排序前顺序");foreach(Student stu in list){Console.WriteLine(stu.name+" "+stu.age.ToString())}Console.WriteLine("请选择:1.按姓名排序;2.按年龄排序")string select =Console.ReadLine();//根据用户选择,生成相应的排序对象switch(select){case "1"://按照姓名排序list.Sort(new NameSort());break;case "2"://按照年龄大小排序,使用重载版本list.Sort(new AgeSort());break; }Console.WriteLine("排序后顺序");foreach(Student stu in list){Console.WriteLine(stu.name+""+stu.age.Tostring());}}
-
集合用于管理在运行时动态创建的元素项。
-
ArrayList在Array的基础上提供了动态的特性。
-
用户可以使用HashTable类将数据,键作为一组来存储,这些数据是根据键进行组织的。
-
集合属于System.Collection命名空间,泛型集合类属于System.Collection.Generic 命名空间。
-
泛型可以提供减少程序的代码量,并能实现安全和提高效率。
-
List< T >是最常用的泛型集合类。
-
实现IComparable接口的类,那么在集合中它的对象就可以使用默认排序方式进行排序。
-
泛型接口提供类型安全,并且简化了实现代码。
-
实现IComparer< T >接口的排序类对象传递给Sort()方法可以根据应用程序的业务逻辑进行相应的排序。
12.LINQ
12.1.LINQ查询表达式
-
LINQ to Odject:数据源为直接或者间接实现了IEnumerable< T >接口的内存数据集合,譬如泛型集合。常用
-
LINQ to Entities:数据源为EntityFramework中的DbQuery< T >类,EntityFramework是目前.NET平台中主流的ORM框架,LINQ to Entities的作用就是帮助我们在EntityFramework上下文中查询实体。
-
LINQ to XML:数据源为XML文档,通过XML文档以XElement.XAttribute类对象的形式加载到内存中,再利用LINQ语法进行数据查询。
List<int> list=new List<int>(){1,7,4,8,13,67} var _list=from i listwhere i<11orderby iselect i结果:1478
其子句中from,where, orderby,select关键字 都是LINQ预定义的关键字。所有的LIQN查询表达式都必须以from子句开始,以select 或group子句结束,在开头子句之间,可以使用where,orderby,join或者其他from 子句.
开始 from [当个数据源元素] in [数据源] 结束 select [目标元素]
微软在System.Core程序集下的System.Linq命名空间下创建了一个名为Enumerabler的静态类,在这个静态类中实现了IEnumberable< T >接口的对象扩展方法。不引入System.Linq命名空间就无法将对应的扩展方法加载到我们的程序中。
12.2.LINQ查询方法
定义在System.Linq命名空间下对应的扩展方法,我们可以直接使用这些扩展方法,这称为LINQ查询方法
List<int> list=new List<int>(){1,5,2,21,34} var _list=list.Where(i=>i<10).OrderBy(i=>1)
LINQ查询方法中要求传入的是委托,按照方法参数中委托定义传入对应的lambda表达式
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSoure> source, Func<TSource,bool>predicate)
此扩展方法要求我们传入一个Func<TSource,bool>委托,此委托返回类型为bool,方法的参数为TSource类型参数,如果满足条件返回true,则保留元素,如果不满足返回false,即将元素过滤掉。
12.3.LINQ查询的延迟加载
using System; using Collections.Generic; using System.Linq; namespace Demo {class Program{static void Main(string[] args){//list数据源List<int>数据类型,继承了IEnumerable<T>接口,所以可以使用LINQList<int> list=new new List<int>(){1,7,4,8,13,57}var _list=list.Where(i=>i<10).OrderBy(i=>i);Console.WriteLine("第1次遍历_list");foreach(var i in _list){Console.WriteLine(i)}list.Add(2)list.Add(3)Console.WriteLine("第2次遍历_list");foreach(var i in _list){Console.WriteLine(i)}}} } 结果:第1次遍历_list1478第2次遍历_list123478
如果LINQ查询是将结果查询到后,存储到了_list指向的内存中,那么两次遍历的结果应该是一样的。
LINQ查询并没有真的在查询语句处进行对数据源的查询,查询发生在遍历迭代数据项时,即所谓LINQ查询延迟加载。LINQ查询仅仅理解成是一个表达式,编译的时候它被存于程序集中,程序运行时,它并没有被拿去执行对数据源的查询
当是我们对查询结果进一步调用ToArray(),ToList()方法之后,返回的数组或者集合会丧失延迟加载的特性,因为这两个操作会在内存中开辟空间,并需要将查询结果存入内存中。
using System; using System.Collections.Generic; using System.Linq; namespace Demo {class Program{static void Main(string[] args){List<int> list=new new List<int>(){1,7,4,8,13,57}var _list=list.Where(i=>i<10).OrderBy(i=>i).ToList();Console.WriteLine("第1次遍历_list");foreach(var i in _list){Console.WriteLine(i)}list.Add(2)list.Add(3)Console.WriteLine("第2次遍历_list");foreach(var i in _list){Console.WriteLine(i)}}} } 结果:第1次遍历_list1478第2次遍历_list1478
LINQ查询被执行了,查询到的结果被存储在了内存中,成为了一个新的数据源,一个与list没关系的数据源,所以即便后来我们再次往list中添加新的数据,对list进行遍历,结果也不会受到影响。
12.4.LINQ查询操作符
12.4.1.LINQ标准查询操作符
System.Linq命名空间下的Enumerable类定义的部分标准查询操作
标准查询操作符 | 功能 |
---|---|
Where | 根据指定条件对数据源进行过滤 |
Select | 把数据源中的元素投射成一个新的对象 |
OrderBy,OrderByDescending,ThenBy,Reverse | 用于改变返回元素的顺序,OrderBy按升序排列,OrderByDescending按降序排列;TheyBy,ThenByDescending在第一次排序的基础上进行第二次排序,TheyBy是升序,ThenByDescending是降序;Reverse反转集合中元素的顺序 |
Any,Contains | Any用于确定集合中是否有满足指定谓词函数的元素;Contains用于检查某个元素是否在集合中元 |
Distinct,Except,Zip | 从集合中删除重复的元素;Except返回只出现在调用集合中的元素,Zip把两个集合合并成一个 |
First,FirstOrDefault,Last,LastOrDefault,Single | First返回数据集合中第一个满足条件的元素,如果没有满足条件的元素会抛出异常;FirstOrDefault类似于First,但是没有满足条件元素的时候,返回类型默认值,不抛出异常;Last返回最后一个满足条件的元素,否则抛出异常,Single返回一个满足体条件的元素,如果有多个元素满足条件则抛出异常 |
Count,Sum,Min,Max,Average | Count用于返回所有元素的个数;Sum用于求和;Min求值最小元素;Max求值最大元素;Average求平均值 |
ToArray,ToList | ToArray将查询结果转成数组;ToList将查询结果转成集合 |
演示
using System; using System.Collections.Generic; using System.Linq;namespace Demo {class Program{static void Main(string[] args){Person p1=new Person("李扬",31);Person p2=new Person("诸葛盼",23)Person p3=new Person("诸葛浪",26)Person p4=new Person("黄超",27);//List<Person>实现了IEnumable<T>接口,可以使用Linq查询List<Person> list=new List<Person>{p1,p2,p3,p4};List<string> listName=list.Where(p=>p.Age>25)//过滤年龄不超过25岁的人.OrderByDescending(p=>p.Age)//按照年龄降序排序.Select(p=>p.Name)//将查询结果投射成名字.ToList();//将结果转成集合Console.WriteLine($"年龄超过25岁共有{listName.Count()}人,按年龄由大到小进行排序结果如下:");foreach(var name in listName){Console.WriteLine(name);}Console.WriteLine($"所有4个人的平均年龄是{list.Average(p=>p.Age)}岁");Console.WriteLine($"所有4个人的总年龄是{list.Sum(p=>p.Age)}岁");Console.WriteLine($"所有4个人中最大的{list.Max(p=>p.Age)}岁");//获取唯一一个年龄小于25岁的人,如果有多个会抛出异常Person pUnder25=list.Single(p=>p.Age<25);Console.WriteLine($"唯一一个年龄小于25岁的是{pUnder.Age}岁的{pUnder25.Name}");}}public class Person{public Person(string argName,int argAge){Name=argName;Age=argAge;}//姓名public string Name{get;set;}//年龄public int Age{get;set;}}} 结果:年龄超过25岁共有3人,按年龄由大到小进行排序结果如下:李杨黄超诸葛浪所有4个人的平均年龄是26.75岁所有4个人的总年龄是107岁所有4个人中最大的31岁唯一一个年龄小于25岁的是23岁的诸葛盼
13.异常处理
13.2.异常
13.2.1.System.Exception
Exception类是所有异常的基类。
13.2.2.try和actch块
14.C#中的文件处理
14.1.System.IO命名空间
.NET框架专门为操作各种流类数据提供了一个名为System.IO的命名空间,该命名空间下包含了许多对各种流数据进行操作的类,还包含一些可以复制,转移,重命名和删除文件和目录的类。读写文件时由于字符的编码有很多种,所以一般还需要使用System.Text这个命名空间下的一些关于字符编码的类。
14.2.File类
File类位于System.IO命名空间。这个类直接继承自System.Object。File类是一个封装类,因此不能被继承。
方法 | 描述 |
---|---|
Create(string filePath) | 在指定路径下创建指定名称的文件,返回一个FileStream对象 |
Copy(string sourceFile,string desFile) | 按指定路径将源文件中的内容复制到目标文件中,如果目标文件不存在将新建目标文件 |
Delete(string filePath) | 删除指定路径的文件 |
Exists(string filePath) | 验证指定路径文件是否存在 |
Move(string sourceFile,string desFile) | 移动文件,如果原文件和目标文件在同一文件夹下则可以实现对文件重命名 |
File.Copy("c:\\aa.txt,"d:\\1.txt");//第一个参数为源文件路径,第二个为目标路径 File.Delete("c:\\aa.txt");
文件复制时如果目标文件夹不存在将引起DiretoryNotFoundException异常,但是删除的文件不存在则不会引发异常
14.3.Directory类
Directory类也是System.IO命名空间的一部分,它包含了处理目录和子目录的静态方法。
方法 | 描述 |
---|---|
CreateDirectory(string path) | 创建目录 |
Delete(string path[,bool recursive]) | 删除指定的目录,如果第二个参数为true则同删除该目标下的所有文件和子目录 |
Exsts(string path) | 测试目录是否存在 |
GetCurrentDiretory() | 获取应用程序的当前工作目录 |
GetDirectories(string path) | 返回代表子目录的字符串数组 |
GetFiles(string path) | 以字符串数组形式返回指定目录中的文件的名称 |
Move(string sourecaPath,string desPath) | 将目录及其内容移到指定的新位置 |
Directory的许多方法的作用于File类似,差别只在于File类操作文件而Directory类操作文件夹:
Directory.Move("c:\\Program","d:\\Program");
14.4.对文本文件的读写操作
-
创建文件流对象。
-
创建流读取对象或者流写入对象。
-
执行读或写操作,调用相应方法。
-
关闭流读取对象或者流写入对象。
-
关闭文件流对象。
14.4.1.文件流
文件流(FileStream)类用于对文件执行读写操作。FileStream构造方法有很多重载方法,构造方法中使用的FileMode,FileAccess和FileShare参数都是枚举类型。
构造方法 | 描述 |
---|---|
FileStream(string filePath) | 接收读写文件的路径和任意一个FileMode枚举值作为参数 |
FileStream(string filePath,FileMode ,FileAccess access) | 接收读写文件的路径与任意一个FileMode枚举值和FileAccess枚举值作为参数 |
FileStream(string filePath,FileMode mode,FileAccess access,FileShare share) | 接收读写文件的路径与任意一个FileMode枚举值和FileAccess枚举值以及一个FileShare枚举值作为参数 |
Append:打开一个文件并将当前位置移到文件末尾,以便能够添加新的数据。如果文件不存在,则新建一个文件。
Create:用指定名称新建一个文件。如果存在同名文件,则改写旧文件。
CreateNew:新建一个文件。
Open:打开一个文件。指定的文件必须已经存在。
OpenOrCreate:如果文件存在就打开,如果不存在就创建并打开。
Truncate:指定的文件必须存在,打开文件并删除文件里的全部内容。同样地,FileAccess参数也是枚举类型。其他成员如下:
Read:用户对指定文件具有只读权限。
Write:用户对指定文件具有只写权限。
ReadWrite:具有读写权限。
FileShare参数也是枚举类型。其枚举值如下:
None:其他用户不能访问文件。
Read:其他用户只能共享对文件执行读操作。
Write:其他用户只能共享文件的写操作。
ReadWrite:其他用户可共享对文件的读写操作。
FileStream fs=new FileStream("c:\\csharp.txt",FileMode.OpenOrCreate,FileAccess.Write); //这段代码打开"c:\\csharp.txt"这个文件,如果文件不存在则创建该文件,并且只能向文件写入数据。
方法 | 描述 |
---|---|
CopyTo(Stream) | 从当前流中读取字节并将其写入到另一流中 |
Close() | 关闭文件流对象 |
Dispose() | 释放由Stream使用的所有资源 |
14.4.2.流读写对象
1.StreamWriter类
创建流写入对象
StreamWriter sw=new StreamWriter(文件流对象);
StreamWriter的构造方法需要一个FileStream类的对象作为参数。
方法 | 描述 |
---|---|
Write() | 将数据写入文件 |
WriteLine() | 将一行数据写入文件 |
Close() | 关闭流写入对象 |
2.StreamReader类
创建流读取对象
StreamReader sr=new StreamReader(文件流对象)
StreamReader的构造方法需要一个FileStream类的对象作为参数。
方法 | 描述 |
---|---|
ReadLine() | 读取一行数据,返回字符串 |
ReadToEnd() | 从当前位置读到末尾,返回字符串 |
Close() | 关闭文件流读取对象 |
using System; using System.Text; using System.IO; namespace StreamText_1 {//写入数据static void WriteRecord(string path){Console.Write("输入要写的内容");string record=Console.ReadLine();//创建文件流FileStream fs=new FileStream(path,FileMode.OpenOrCreate,FileAccess.Write,FileShare.None);//创建流写入对象StreamWriter sw=new StreamWriter(fs);//执行方法,将内容写入文件sw.Write(record);//关闭流写入对象sw.Close();//关闭文件流fs.Close(); }//读取数据static void ReadRecord(string path){FileStream fs=new FileStream(path,FileMode.Open,FileAccess.Read,FileShare.None);//创建流读取对象StreamReader sd=new StreamReader(fs);//执行方法,读取文本数据string record=sd.ReadToread();//关闭流读取对象sd.Close();//关闭文件流fs.Close();Console.WriteLine("读取成功!内容如下:\n"+record);}static void Main(string[] args){Console.WriteLine("请选择:")Console.WriteLine("1.读取数据 2.写入数据");Console.ReadLine();if(f=="1"){readRecord("d:\\test.txt");}else if(f=="2"){WriteRecord("d:\\test.txt");}} }
14.6.序列化和反序列化
更多推荐
C#基础语法学习
发布评论