C#程序设计教程 - 第1章 C#程序设计基础

第1章 C#程序设计基础

1.1 C#基础

    概述
  • .NET是一个平台
  • C#是一门语言
    编译原理
  • 编写源代码
  • C#编译器将C#源代码编译成MSIL
  • 将中间代码交给CLR的即时编译器(JIT)将微软中间语言转化成相应的机器码(CPU指令)
  • 交给CPU运行机器码;

    被CLR所管理的代码叫做托管代码,不能够被CLR管理的代码叫做非托管代码,非托管代码运行效率高,但是相对安全性较低;托管代码运行效率较低,但代码相对是安全的。

    标识符
  • 以字母/下划线/数字组成,且第一个字符必须为字母或下划线
  • 标识符严格区分大小写
    命名规范
  • // 单行注释
  • /**/ 多行注释
  • ///文档注释:可以生成提示
  • 变量命名:camel:第一个单词首字母小写,后面每一个单词的首字母大写。按功能/方法的返回值命名。
  • 对于类和方法/属性:pascal命名,第一个单词首字母大写,之后的单词首字母大写GetMax()、ChangeName()。一般使用动词。
  • 代码需要缩进与对齐
  • 变量的名称一般是名词:说明这个变量有什么用。
  • 方法:一般是动词,说明这个方法可以执行什么样的操作。
  • 在定义变量的时候,长短不是关键,意义才是关键。
    C#数据类型
  • 附件:C#数据类型.xmind
  • 注意:string类型表示一个字符序列(0个或多个Unicode字符)。string是.NET Framwork中String的别名。尽管string是引用类型,但定义相等运算符(==和!=)是为了比较string对象(而不是引用)的值。这使得对字符串相等性的测试更为直观。string对象存储在堆空间中,当两个对象的值相等时,他们是公用一个堆空间中的值,只有当值改变时,才会再次开辟空间;
  • 值类型数据存放在栈空间内:数值类型、char、bool、枚举、结构
  • 引用类型 存放在堆空间内:数组、string、类
  • 值类型的传递,传递的是这个变量的副本
  • 引用类型的传递,传递的是存储的堆空间的地址
  • 结构补充:
    • a.结构是值 类继承自ValueType型
    • b.它的使用方式和类很相似,但是也有一些区别:
    • 结构中不能为字段赋值
    • 结构不能包含显式的无参数构造函数
    • 如果通过构造函数为结构成员赋值,只能赋值给字段。
    • 如果不使用new来创建结构,那么就必须为结构中的字段赋值。
    • 结构是一个值类型,所以在做为参数传递的时候传递是值的副本,与引用类型不一致
    • 值类型做参数传递的时候会开辟新的空间来存储值,同时在同引用类型做转换的时候需要 装箱和拆箱操作,消耗系统资源
  • 枚举补充:
  • 固定的一些值:字符串值、整型值
public enum Gender{男=0,女=1}

2.如果做类型转换:

int num=(int)Gender.男;

Gender gen=(Gender)Enum.parse(TypeOf(Gender),"男");

类型转换

? 隐式转换

系统默认的不需要加以声明就可以进行转换

? 显示转换

显示转换又称为强制类型转换

(类型表示符)表达式

例如:(int)1.23 //把double类型的1.23转换为int类型,结果为1

注意:int.parse()只能转换string类型的,当参数为null时会报异常;

System.Console.WriteLine("固定点格式2度 {0}", d.ToString("F2"));//1234.56

Or

d.ToString("0.00");

垃圾回收机制

栈空间里的变量什么时候被清理:栈空间里的变量一旦出了变量的作用域就会被系统回收;堆空间里的对象什么时候被回收:堆空间里面的对象,当没有任何变量指向它的时候就被标记为“垃圾”,表示可以被回收,被垃圾回收器(GC)来定时回收,标记为可回收的对象GC.Collect();//调用立即回收方法,一般不建议使用这个方法。

异常处理

try

{

//...

}

catch (Exception ex)

{

throw;

}

finally

{

//此代码一定会执行,一般进行释放资源的操作

}

局部变量与成员变量

局部变量:定义在方法里面的变量就叫做局部变量;没有默认的初始值,使用变量之前必须给它赋值。

成员变量:定义在类下面的变量叫做成员变量;如果是数值类型默认初始值为0 如果是引用类型默认初始值为空。

软件设计中的几个原则:

封装变化点

开放封闭原则(对修改封闭,对扩展开放)---添加单独的功能—添加单独的方法;添加单独的角色---添加单独的类

高内聚,低藕合 ---三层---EF MVC

1.2 变量和常量

在程序执行过程中,其值不发生改变的量为常量,其值可变的量称为变量。

在程序中,常量是可以不经说明而直接引用的,而变量则必须先定义后使用。

变量

n 变量定义

[访问修饰符] 数据类型变量名 [=初始值];

注意:C#中不允许使用未初始化的变量

变量默认初始值:

任何数值类型的初始值:0(0.00)

string 的默认初始值:空字符串

object 的默认初始值:null

bool 的默认初始值:false

n 值类型与引用类型的变量

值类型赋值是拷贝

引用表示所使用的是变量对象的地址而不是变量或者对象本身;当声明引用类型变量时,程序只是分配了存放这个引用的存储空间;要想创建对象并把对象的存储地址赋给该变量,就需要使用New操作符;

例如: MyClass my; my = new MyClass();

当new操作符来创建对象是,C#会在堆空间为这个对象分配足够的空间来存放MyClass类对象的实例,然后把这个实例的地址赋给这个引用类型的 my;

例如:


int[] a = newint[3] { 1, 2, 3 };

for (int i = 0; i < 3; i++)

{

Console.Write("{0}", a[i]);

}

Console.WriteLine();

int[] b = a;

for (int i = 0; i < 3; i++)

{

b[i] = 2 * b[i];

}

for (int i = 0; i < 3; i++)

{

Console.Write("{0}", a[i]);

}

Console.ReadKey();

//输出结果 123 246

//都是输出a[i],说明改变的是同一个内存中的数据,只不过引用变量不同,但指向的是同一个堆空间中的地址

常量

n 定义

常量的类型可以是任何一种值类型或引用类型。

n 直接常量

直接常量是指把程序中不变的量直接硬编码为数值或字符串值。

例如:

-100 //整型直接常量

"aaa" //字符串型常量

true //bool型常量

null //对象引用常量

1.23e5 //浮点型直接常量

n 符号常量

符号常量是通过关键字const声明的变量,包括常量的名称和它的值。

const 数据类型常量名 = 初始值

注意:在程序中,常量只能被赋予初始值,一旦赋予一个常量初始值,这个常量的值在程序的运行中就不允许被改变,即无法对一个常量赋值。

C#编译器在编译的时候会将使用到常量的地方直接替换为常量的值,不会在程序集里面保留声明的常量名

1.3 装箱和拆箱

装箱转换

装箱转换是指将一个值类型的数据隐式地转换成一个对象类型的数据;把一个值类型装箱,就是创建了一个object类型的事例,并把该值类型的值复制给这个object实例。

例如:int i = 8; object obj = i;

在执行装箱转换时,也可以使用显示转换

例如:int i = 1; object obj = (object)i;

拆箱转换

拆箱转换是指将一个对象类型数据显式地转换成一个值类型数据。

拆箱分2步:首先检查对象的实例,确保它是给定值类型的一个装箱值,然后把实例的值复制到值类型数据中

例如:object obj = 2;int i = (int)obj;

注意:拆箱转换需要(而且必须)执行显示转换

1.4 形式参数和实际参数

? 函数的参数分为形参和实参两种。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。

? 形式参数(简称形参)是在用户自定义函数过程(Function)、子过程(Sub)过程名后圆括号中出现的变量名,多个形参用逗号分割。

? 实际参数(简称实参)是在调用上述过程时,在过程名后的参数,其作用是将它们的数据(值或地址)传送给被调过程对应的形参变量。

? 形参可以是:变量,带有一对括号的数组名。

? 实参可以是:同类型的常数、变量、数组元素、表达式、数组名(带有一对圆括号)。

n 函数的形参和实参具有以下特点: 

? 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。

? 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。

? 实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。

? 函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。

1.5 C#运算符

算术运算符

+ - * / % ++ --

字符串运算符

+

赋值运算符

=

+= 加赋值a+=b == a=a+b

-= 减赋值a-=b == a=a-b

*= 乘赋值a%=b == a=a*b

/= 除赋值a/=b == a=a/b

%= 取模赋值a%=b == a=a%b

<<= 左移赋值 a<<=b == a=a<<b

>>= 右移赋值 a>>=b == a=a>>b

&= 与赋值a&=b == a=a&b

^= 异或赋值 a^=b == a=a^b

|= 或赋值 a|=b == a=a|b

关系运算符

关系运算符是二元运算符,用于比较两个云算数,当结果为true或非0时,对应值为1,否则为0;

<,<= ,>,>= , == , !=

逻辑运算符

逻辑运算符用于bool值,结果为true或false

! 逻辑非 !(2<3) 返回false

&&逻辑与

|| 逻辑或

位运算符

位(bit)是计算机中表示信息最小的单位,一般用0和1表示.8个位组成一个字节;

位运算符的运算对象必须为整数;

~ 按位求反!2

<<左移 8<<2

>>右移 8>>2

&按位与8&5

| 按位或 8&5

条件运算符

条件运算符是一个3元运算符,每个操作数同时又是一个表达式的值;

表达式1 ? 表达式2 :表达式3

表达式1 为true 返回表达式2 ,否则返回表达式3

其他运算符

Is运算符

Is运算符用于检查表达式是否是制定的类型,如果是,其结果为true,否则结果为false

例如: double d = 1.23;

Bool t1 = d is int; //t1 = false

Bool t2 = d id double; //t2 = true;

Sizeof运算符

Sizeof运算符求值类型数据在内存中占用的字节数;

Sizeof(类型标识符)

Typeof运算符

该运算符用于获得制定数据类型的说明;

Console.Write(“{0}”,typeof(Student));

New运算符

该运算符用于创建一个类的实例

1.6 C#中常用类

String类MSDN

using System;

字符串是一个字符数组

字符串是一种特殊的引用类型,具有恒定性及不可变性

字符串是不可变的,当我们创建一个新的字符串对象的时候,首先会去字符串池里检查是否有同样的对象存在,如果有,直接指向这个对象,如果没有则创建1个新的对象

当我们修改1个字符串对象的时候,不是去修改这个字符串变量指向的对象,而是重新新建一个字符串对象将这个变量指向它

System.Text.StringBuilder当我们进行大批量的字符串拼接操作时建议使用StringBuilder用此对象效率更高(修改字符串而不创建新的对象,在System.Text类下)

字符串的本质就是一个字符数组

字符串的定义:string str=”” string str=new string(new char[]{});

字符是不可变的---字符串是静态文本值,如果对字符串值做修改,系统会重新为其分配一块空间,但是原有的空间也不会被收回,而是继续放在常量区,直到应用程序结束才收回

String类的常用属性

l Chars: 获取当前 String 对象中位于指定字符位置的字符

l Length: 获取当前 String 对象中的字符数

String类的常用方法及说明

l Compare 静态方法;比较两个制定的String对象

l CompareTo 非静态方法;将此字符串与制定的对象或String进行比较,并返回两者相对值的指示;

l Concat 静态方法;连接String的一个或多个字符串

l Contains 非静态方法;返回一个值,该值指示制定的String对象是否出现在此字符串中;

l Equals 非静态方法;确定两个String对象是否具有相同的值;

l Format 静态方法;将制定的String中的每个格式项替换为相应对象的值的文本等效项;

l IndexOf 非静态方法;返回String或一个或多个字符在此字符串中的第一个匹配项的索引;

l Insert 非静态方法;在该String中的制定索引位置插入一个制定的String

l Remove 非静态方法;从该String中删除制定个数的字符;

l Replace 非静态方法;将该String中的制定String的所有匹配项代替为其他制定的String

l Split 非静态方法; 返回包含该String中的字符串(由制定Char或String数组的元素分隔)的String数组;

l SubString 非静态方法;从此字符串中检索子字符串;

l ToLower 非静态方法;返回该String转换为小写的副本

l ToUpper 非静态方法;返回该String转换为大写的副本;

l Trim 非静态方法;从此字符串的开始位置和末尾位置移除一组制定字符的所有匹配项

Math类MSDN

Math类位于System命名空间中,包含了常用的算术运算功能方法,这些都是静态方法,可通过”Math.方法名(参数)”使用

n Abs //绝对值

n Ceiling //返回大于或等于数字的最小整数

n Floor //返回小于或等于数子的最大整数

n Max //返回两个数中的较大的一个

n Min //返回两个数中的较小的一个

n Sqrt //返回制定数字的平方根

n Truncate //计算一个数字的整数部分

Convert类MSDN

Convert类位于System命名空间中,用于将一个值类型转换成另外一个值类型;这些方法都是静态方法;”Convert.方法名(参数)”使用

Convert不仅仅转换还是改造。

n ToBoolean

n ToDataTime

n ToInt32

n ToNumber //将数据转换为Double类型

n ToObject //

n ToString //

Convert.ToInt32(false);//运行结果是0

Convert.ToInt32(true); //运行结果是1

Convert.ToInt32(null); //运行结果是0

DateTime结构MSDN

DateTime位于System命名空间中,DateTime值类型取值在公元00001-01-01 12:00:00 ~ 9999-12-31 23:59:59之间的日期和时间;

DateTime dt = new DateTime(年,月,日,时,分,秒);

DateTime结构的常用属性:

n Date //获取此实例的日期部分

n Day //获取此实例所表示的日期为该月中的第几天

n DayOfWeek //获取此实例所表示的日期是星期几

n DayOfYear //获取此实例所表示的日期是该年中的第几天

n Hour //获取此实例所表示日期的小时部分

n Millisecond //获取此实例所表示日期毫秒部分

n Minute //获取此实例所表示日期分钟部分

n Month //获取此实例所表示日期月份部分

n Now //获取一个DateTime对象,该对象设置为此计算机上的当前时间

n Second //获取此实例所表示日期的秒部分

n TimeOfDay //获取此实例的当天时间

n Today //获取当前日期

n Year //获取此实例所表示日期的年份部分

DateTime结构的常用方法:

n AddDays //非静态方法 将制定的天数添加到此实例的值上

n AddHours //非静态方法 将制定的小时添加到此实例的值上

n AddMilliseconds //非静态方法 将制定的毫秒添加到此实例的值上

n AddMinutes //非静态方法 将制定的分钟添加到此实例的值上

n AddMonths //非静态方法 将制定的月份添加到此实例的值上

n AddSeconds //非静态方法 将制定的秒数添加到此实例的值上

n AddYears //非静态方法 将制定的年份添加到此实例的值上

n Compare //静态方法 比较两个DateTime的实例,并返回它们相对值的指示

n CompareTo //非静态方法 将此实例与制定的对象或值类型进行比较,并返回二者相对值的指示

n DaysInMonth //静态方法 返回制定年和月中的天数

n IsLeapYear //静态方法 返回制定的年份是否为闰年的指示

n Parse //静态方法 将日期和时间的制定字符串表示转换成其等效的DateTime

/*

string常用方法-Format

string.Format("{0:d}",dt);//2005/11/5

string.Format("{0:D}",dt);//2005年11月5日

string.Format("{0:f}",dt);//2005年11月5日 14:23

string.Format("{0:F}",dt);//2005年11月5日 14:23:23

string.Format("{0:g}",dt);//2005-11-5 14:23

string.Format("{0:G}",dt);//2005-11-5 14:23:23

string.Format("{0:M}",dt);//11月5日

string.Format("{0:R}",dt);//Sat, 05 Nov 2005 14:23:23 GMT

string.Format("{0:s}",dt);//2005-11-05T14:23:23

string.Format("{0:t}",dt);//14:23

string.Format("{0:T}",dt);//14:23:23

string.Format("{0:u}",dt);//2005-11-05 14:23:23Z

string.Format("{0:U}",dt);//2005年11月5日 6:23:23

string.Format("{0:Y}",dt);//2005年11月

string.Format("{0}",dt);//2005-11-5 14:23:23

string.Format("{0:yyyyMMddHHmmssffff}",dt);

*/

Console.WriteLine(string.Format("{0:yyyyMMddHHmmssffff}", DateTime.Now));

Console.WriteLine(DateTime.Now.ToString("yyyyMMddHHmmssffff"));

备注:yyyyMd可以去除0,例如:20140902->201492

1.7 C#控制语句

选择控制语句

n If语句

If ( 条件表达式 ) 语句;

n If … else 语句

If ( 条件表达式 )

语句 1;

Else

语句 2;

n If … else if 语句

If ( 条件表达式式1 ) 语句 1;

Else if ( 条件表达式2 ) 语句 2;

Else if ( 条件表达式n ) 语句 n;

Else 语句 n+1;

n Switch语句

Switch ( 表达式 )

{

Case 常量表达式1:语句1;

Case 常量表达式2:语句2;

Case 常量表达式3:

Case 常量表达式4:

语句4;

Break;

Case 常量表达式n:语句n;

Default :语句n+1;

}

记得添加(Default也要添加)break ;

循环控制语句

n While

While ( 条件表达式 ) 语句;

n Do – while

Do 语句; while ( 条件表达式 );

n For语句

For( 表达式1;表达式2;表达式3; ) 语句;

跳转语句

n Break

Break 语句使程序从当前的循环语句(do while 和 for)内跳转出来,接着执行循环语句后面的语句;

n Continue 语句

Continue 也用于循环语句,但它不是结束循环,而是结束循环语句的当前一次循环,接着执行下一次循环;

n Goto语句(较少使用)

Goto语句可以跳出循环和switch语句;goto语句用于无条件转移程序的执行控制,它总是与一个标号相匹配;

Goto 标号;

“标号”是一个用户自定义标识符,它可以处于goto语句前面,也可以处于其后面.但是标号必须与goto语句处于同一个函数中.定义标号时,由一个标识符后面跟一个冒号组成;

? 示例

While(true)

{

I++;

If (I > 10) goto end;

}

End :Console.Write(“OK”);

1.8 数组

所有存储结构中效率最高的,因类它在内在中是一串连续的空间

数组分为一维数组,二维数组,和三维数组,通常把二维数组称为矩阵,三维及以上的数组称为多维数组;

数组:同样一种类型的数据可以存放指定个数的一组数据,存储的数据称为元素。

数组声明时必须要指定数组的长度;数组里的每一个元素的数据类型都是一致的;数组的长度必须在声明时指定,一旦声明将不能被更改。

数组是所有存储结构最快的,因为它是连续的存储空间;

一维数组

数组类型[] 数组名;

n 一维数组的动态初始化

动态初始化需要借助new运算符,为数组元素分配内存空间,并为数组元素赋值,数组类型初始化为0,布尔类型初始化为false,字符串类型初始化为null;

动态初始化格式

数组类型[] 数组名= new 数组类型[n] { 元素0,元素1, … ,元素n-1 };

n 一维数组的静态初始化

静态初始化数组时,必须与数组定义结合在一起,否则会出错;

静态初始化格式

数据类型[] 数组名= { 元素0,元素1, … ,元素n-1 };

n 访问一维数组中的元素

For

Foreach

例如:

int[] arr=new int[5]; 新定义一个数组长度为5(常用)

int[] arr1=new int[]{0,1,2,3,4}; 定义一个数组,里面的元素为0,1,2,3,4

int[] arr2=new int[5]{0,1,2,3,4};定义一个数组,里面的元素为0,1,2,3,4 长度为5

int[] arr3={1,2,3,5,4}; 不通过new创建数组(常用)

赋值:arr[0]=100;

取值:int x =取值通过arr[1];

int? count = 0;

int?[] intArr = newint?[] { 0, 1, 3, null, 5 };

for (int i = 0; i < intArr.Length; i++)

{

if (intArr[i] != null) //对于引用类型象数组才有用(string) //int类型始终不等与int?,int类型始终不等于null

{

count += intArr[i];

}

}

MessageBox.Show(count.ToString());

二维数组

数组类型[,] 数组名;

n 二维数组的动态初始化

数组类型[,] 数组名 = new 数据类型[m][n] {

{元素0, 0, 元素0,1, … , 元素0,n-1 }},

{元素1, 0, 元素1,1, … , 元素1,n-1 }},

{元素m-1, 0, 元素m-1,1, … , 元素m-1,n-1 }}

};

M,n分别为行数和列数

可以省略数组长度

Int[,] = new int[2,3]{{1,2,3},{4,5,6}};

Int[,] = new int[,]{{1,2,3},{4,5,6}};

Int[,] arr = new int[x,y];

x:表示行数

y:表示列数

for遍历也是先遍历行数,再遍历列数,GetLength(0),GetLength(1)

for和foreach都可以循环出数据

例如:


int[,] mm = newint[3, 4] {

{ 1, 2,3,4 },

{ 11, 12 ,13,14},

{ 111, 121,131,141 } };

for (int i = 0; i < mm.GetLength(0); i++)

{

for (int j = 0; j < mm.GetLength(1); j++)

{

Console.WriteLine(mm[i, j]);

}

}

foreach (int i in mm)

{

Console.WriteLine(i);

}

n 二维数组的静态初始化

数组类型[,] 数组名 = {

{元素0, 0, 元素0,1, … , 元素0,n-1 }},

{元素1, 0, 元素1,1, … , 元素1,n-1 }},

{元素m-1, 0, 元素m-1,1, … , 元素m-1,n-1 }}

};

例如:


//访问二维数组中的元素

int[,] arr2 = { { 1, 2, 3 }, { 4, 5, 6 } };

for (int i = 0; i < 2; i++)

{

for (int j = 0; j < 3; j++)

{

Console.Write("{0}", arr2[i, j]);

}

}

foreach (int i in arr2)

{

Console.Write("{0}", i);

}

Console.WriteLine();

//arr2.GetLength(int index); //得到指定维度的长度;

for (int i = 0; i < arr2.GetLength(0); i++)

{

for (int j = 0; j < arr2.GetLength(1); j++)

{

Console.Write("{0}", arr2[i, j]);

}

}

Console.WriteLine("取值{0},{1};维度{2}", arr2[1, 1], arr2.GetValue(1, 1), arr2.Rank);

Console.ReadKey();

//输出结果

//123456123456

//123456取值5,5;维度2

int? count0 = 0;

int? count1 = 0;

int?[] intArr = newint?[] { 0, 1, 3, null, 5 };

for (int i = 0; i < intArr.Length; i++)

{

if (intArr[i] != null) //对于引用类型象数组才有用(string) //int类型始终不等与int?,int类型始终不等于null

{

count0 += intArr[i];

}

}

foreach (object var in intArr)

{

if (var != null)

{

count1 += int.Parse(var.ToString());

}

}

MessageBox.Show("for嵌套循环==foreach:" + count0.ToString() + "," + count1.ToString());

交错数组

交错数组是元素为数组的数组,与多维数组类似,所不同的是,交错数组元素的维度和大小可以不同,而多维数组的均相同

交错数组的本质就是一个一维数组,只不过这个数组里面的每一个元素值又不一个数组

交错数组的元素需要先实例化,因为数组是一个引用类型的变量,需要先实例化再使用,如果没有实例化会出现 “未将对象引用设置到对象的实例”

因为交错数组的每一个元素类型是数组,所以遍历的时候就可以按遍历一维数组的方式一考虑

示例

Int[][] arr = new int[3][];

必须初始化才能使用

Arr[0] = new int[5];

Arr[1] = new int[4];

也可以填充数据

Arr[0] = new int [3] {1,2,3};

也可以在数组生命时初始化

Int[][] arr = {

New int[]{1,2,3},

New int[]{4,4,5}

};

Int[][] arr = new int[][]{

New int[]{1,2,3},

New int[]{4,4,5}

};

int[][] arr = newint[2][];

//数组的每个元素又是一个数组对象,所以需要先实例化数组对象

arr[0] = newint[] { 1, 2, 3 };

arr[1] = newint[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

for (int i = 0; i < arr.Length; i++)

{

for (int j = 0; j < arr[i].Length; j++)

{

//Console.Write(arr[i][j] + "");

MessageBox.Show(arr[i][j] + ",");

}

}

n 访问交错数组

For嵌套查询

1.9 集合

Array 类

Array类是所有数组类型的抽象基类型,它提供了创建,操作,搜索和排序数组的方法.在C#中数组实际上是对象;

Array类的属性个和方法

n 常用属性

Length

LongLength

Rank //获取Array的秩(维数)

n 常用方法

BinarySearch

Copy

CopyTo

Find

ForEach

GetLength

GetLongLength

GetLowerBound

GetUpperBound

GetValue

IndexOf

Resize

Reverse

SetValue

Sort

Array 类中方法的使用

ArrayList 类

该类位于命名空间Syestem.Collections中,用于建立不定长度的数组,由于该类数组的数据类型为Object,因此在数组中的元素可以是任何类型的数据;由于其长度不固定,可以将其对象看成是一个集合;

ArrayList 数组名= new ArratList(); //可以看成是一个动态数组

n ArratList类的属性

Capacity //获取或设置ArrayList可包含的元素数

Count //获取ArrayList中实际包含的元素数

Item //获取或设置指定索引处的元素

n ArrayList类的方法

Add

AddRange

BinarySearch

Clear

Clone

Contains

Copyto

GetRange

InsexOf

Inset

InsetRange

LastIndexOf

Remove

SetRange

Sort

ToArray

ToString

TrimToSize

n ArrayList 存储原理:

存储数据的是1个object类型的数组,这个对象初始化的时候,这个数组的长度为0;

当我们往其中新增一个元素的时候,会将这个数组的长度变为4,然后将元素加入到数组里面,当进行其他次的添加时,如果数组满了会新建一个数组长度是原来数组的2倍。

查询:foreach for遍历

不足:

1.如果存储值类型,需要装箱和拆箱操作

2.不管什么类型的数据,存储之后,取出来使用必须通过类型转换

List<T>类

List<T>类提供用于对列表进行搜索,排序和操作的方法.该类的定义放在命名空间System.Collections.Generc中,其中T为列表中元素的类型

List<T>类体ArrayList类的泛型等效类,该类使用大小可按需动态添加的数组实现IList泛型接口

List<T>数组名= new List<T>();

n List<T>的属性

Capactity //获取或这是该内部数据结构在不调整大小的情况下能够保存元素总数

Count //获取List中实际包含的元素数

Item //获取或设置指定索引处的元素

n List<T>的方法

Add

AddRange

BinarySearch

Clear

Clone

Contains

Copyto

Exists

Find

FindAll

FindIndex

FindLastIndex

ForEach

InsexOf

Inset

InserRange

LastIndexOf

Remove

RemoveAll

RemoveAt

RemoveRange

Reverse

Sort

ToArray

TrimExcess


int[] nums = newint[] { 3535, 5, 34, 534234324, 32, 5435, 3, 423, 4, 54 };

List<int> list = newList<int>();

for (int i = 0; i < nums.Length; i++)

{

//if (nums[i] % 2 != 0)

//{

// list.Add(nums[i]);

//}

list.Add(nums[i]);

}

list.Sort();

for (int i = 0; i < list.Count; i++)

{

Console.WriteLine(list[i]);

}

Console.Read();

Hashtable

1) 在collections类下

using System.Collections;

Hashtable table=new Hashtable();//哈希表 键值对 键和值都是Object类的

table.Add("name","LiuDeHua");//新增键不可以重复

table["name"]="LiMing";//修改

table.Add=("person",new Person());//取值 根据键取值

Person p= table["person"] as Person; //放进去是一个object类,取出来时也是一个object,需要进行类型转换

table.Remove("person"); //删除 根据键删除

//遍历HashTable的两种方式

object obj=table["Person"];

foreach(string key in table.Keys)//键的集合

{

Console.WriteLine(table[key]);

}

foreach(string value in table.Values)//值的集合

{

Console.WriteLine(table[value]);

}

2) 哈希表存储数据的原理:哈希是一种算法

哈希表存储数据是一个数组 backet结构数组(key,value,hase_coll),输入key之后根据一定的算法(哈希算法)算出key值应该存储的下标

3) 哈希表的某些方法:

table.ContainsKey();//返回布尔值

4) 什么时候用?

需要频繁的取单个值,则可以选用hashtable;

若是只需要存进去,能遍历就可以用集合

泛型集合

List<T>

Dictionary<Tkey,TValue>

using System.Collections.Generic;

List<T>类似于ArrayList—长度动态变化的数组

Dictionary<K,V>类似于Hashtable

推荐使用泛型集合?原因????

T,K,V就像一把锁,锁住集合只能存某种特定的类型,这里的T,K,V也可以是其它字母

泛型集合可以进行foreach遍历,是因为实现了IEnumerable<T>具有了GetEnumerator()方法

先看foreach的本质

<这里写指定的数据类型(包括类)>,尖括号里面指定是什么类型就要存入什么类型

List<Person> list=new List<Person>();//对应arraylist

增加:list.Add(i);

取值:list[1];

遍历:foreach(Person p in list){}

哈希值的泛型集合:

Dictionary<string,string> dic=new Dictionary< string,string> //对应哈希值

dic["name"]; 取值

三种遍历方式:

foreach(string key in dic.Keys) //以键遍历

{

console.WriteLine(dic[key]);

}

foreach(string value in dic.Values) //以值遍历

{

console.WriteLine(dic[value]);

}

foreach(KeyValuePair< string ,Person> kvp in dic)//以键值对来遍历 //遍历哈希表中的所有键值对

{

console.WriteLine(kvp);

}

List<T>:相当于一个长度动态变化的数组:长度任意扩展,但是有数组类型规范

命名空间:using System.Collections.Generic;

创建:List<T> lists=new List<T>();T是指用户自定义的类型,可以是任意的类型,如 List<int> list=new List<int>();说明这个集合里面只能存放整型类型的数据

通过索引删除和访问及修改

Dictionary<key,value>补充:

Dictionary<key,value>:相当于规范了类型的hashTable

定义:Dictionary<string, string> dics = new Dictionary<string, string>();

添加元素:只能通过Add(),如:

dics.Add("aa", "aa");

dics.Add("bb", "bb");

删除:通过key值删除

遍历:三种遍历方法:遍历key,遍历value,通过类型DictionaryEntry遍历

使用总结

1. Arraylist与List<T>:本质上来说,存储数据是就是object[]

a) 长度是可变的 length:长度 capacity:容量,capacity不能小于实际内容的值,当你将length变大之后,capacity会自动变大。

b) 它的类型是object,写入和读取的时候需要做装箱和折箱

c) 增加:Add(object)

d) 删除:Remove(object) RemoveAt(int index)

e) 集合通过索引下标访问

f) clear();清空

g) Contains():判断是否包含某一个元素

h) ToArray();

不足:

l 如果存储值类型,需要装箱和拆箱操作

l 不管什么类型的数据,存储之后,取出来使用必须通过类型转换

1. HashTable与Dictionary<Key,Value>

a) 通过Add()方法添加元素,需要同时添加key和value

b) 通过key访问和删除

c) 数据其实是存放于一个burket[]中的

key

value

hash_coll:它是由key通过算法得到,用来标明数据存储的地址空间,在遍历的时候通过这个hash_coll值取出对应的value,所以它的存取效率非常高,因为不需要像arrlist做遍历

2. 有三种遍历方式:

foreach(object obj in ht.Keys)

{

Console.WriteLine(obj);

}

--------------------------------------------

foreach (object obj in ht.Values)

{

Console.WriteLine(obj);

}

---.----------------------------------------------

foreach (DictionaryEntry obj in ht)

{

Console.WriteLine(obj.key +” : “+obj.value);

}

foreach (KeyValuePair<Tkey,Tvalue> obj in ht)

{

Console.WriteLine(obj.key +” : “+obj.value);

}

不足:1.如果存储值类型,需要装箱和拆箱操作 2.不管什么类型的数据,存储之后,取出来使用必须通过类型转换

所以可以这样认为:

List<T>和Dictionary<key,value>规范了类型,在做遍历的时候不需要做类型转换,同时也了数据安全。

1.list<T>与ArrayList:使用索引访问和删除,也可能通过对象删除,使用Add进行对象的添加,也可以通过AddRange()一次性添加多个元素

2.HashTable与Dictionary<key,value>:通过key值访问和删除,遍历的时候可以选择遍历key或者遍历value.在添加元素的时候需要添加key值和value值

1.10 Foreach遍历原理

1) 实现了IEnumerable接口,接口里面定义了GetEnumerator的方法,这个要求返回实现了IEnumerator(迭代器)的对象

IEnumerator tor=list.GetEnumrator();//枚举器 遍历器 迭代器

while(tor.MoveNext()) //调用迭代器的MoveNext方法(将当前指针往下移一位)判断其返回值

{

Console.WriteLine(tor.Current);//其返回值为true则调用迭代器的Current属性

}

p=new Person(){Name="张三"}

2) foreach(person p in pc)

第一步: IEnumerable able=p as IEnumerable; //转换为一个迭代器)

第二步: IEnumerator tor=able.GetEnumerator();//调用迭代器的GetEnumerator()方法,返回一个IEnumerator对象

第三步: while(tor.MoveNext()) //调用IEnumerator对象的MoveNext方法(将当前指针往下移一位)判断其返回值

{

Console.WriteLine(tor.Cunrrent);//其返回值为true则调用迭代器的Current属性

}

第1章    C#程序设计基础
1.1    C#基础
概述
.NET是一个平台
C#是一门语言
编译原理
1)    编写源代码
2)    C#编译器将C#源代码编译成MSIL
3)    将中间代码交给CLR的即时编译器(JIT)将微软中间语言转化成相应的机器码(CPU指令)
4)    交给CPU运行机器码
?    被CLR所管理的代码叫做托管代码,不能够被CLR管理的代码叫做非托管代码,非托管代码运行效率高,但是相对安全性较低;托管代码运行效率较低,但代码相对是安全的。
标识符
?    以字母/下划线/数字组成,且第一个字符必须为字母或下划线
?    标识符严格区分大小写
命名规范
?    // 单行注释
?    /**/ 多行注释
?    ///文档注释:可以生成提示
?    变量命名:camel:第一个单词首字母小写,后面每一个单词的首字母大写。按功能/方法的返回值命名。
?    对于类和方法/属性:pascal命名,第一个单词首字母大写,之后的单词首字母大写GetMax()、ChangeName()。一般使用动词。
?    代码需要缩进与对齐
?    变量的名称一般是名词:说明这个变量有什么用。
?    方法:一般是动词,说明这个方法可以执行什么样的操作。
?    在定义变量的时候,长短不是关键,意义才是关键。
C#数据类型
?    C#数据类型.xmind
注意:string类型表示一个字符序列(0个或多个Unicode字符)。string是.NET Framwork中String的别名。尽管string是引用类型,但定义相等运算符(==和!=)是为了比较string对象(而不是引用)的值。这使得对字符串相等性的测试更为直观。string对象存储在堆空间中,当两个对象的值相等时,他们是公用一个堆空间中的值,只有当值改变时,才会再次开辟空间;
值类型数据存放在栈空间内:数值类型、char、bool、枚举、结构
引用类型 存放在堆空间内:数组、string、类
值类型的传递,传递的是这个变量的副本
引用类型的传递,传递的是存储的堆空间的地址
结构补充:
1.1    结构是值 类继承自ValueType型
1.2    它的使用方式和类很相似,但是也有一些区别:
?    结构中不能为字段赋值
?    结构不能包含显式的无参数构造函数
?    如果通过构造函数为结构成员赋值,只能赋值给字段。
?    如果不使用new来创建结构,那么就必须为结构中的字段赋值。
?    结构是一个值类型,所以在做为参数传递的时候传递是值的副本,与引用类型不一致
?    值类型做参数传递的时候会开辟新的空间来存储值,同时在同引用类型做转换的时候 需要 装箱和拆箱操作,消耗系统资源
枚举补充:
固定的一些值:字符串值、整型值
    1.public enum Gender{男=0,女=1}
    2.如果做类型转换:
        int num=(int)Gender.男;
        Gender gen=(Gender)Enum.parse(TypeOf(Gender),"男");
类型转换
?    隐式转换
系统默认的不需要加以声明就可以进行转换
?    显示转换
显示转换又称为强制类型转换
(类型表示符)表达式
例如:(int)1.23    //把double类型的1.23转换为int类型,结果为1
注意:int.parse()只能转换string类型的,当参数为null时会报异常;
System.Console.WriteLine("固定点格式2度 {0}", d.ToString("F2"));//1234.56
Or
d.ToString("0.00");
垃圾回收机制
栈空间里的变量什么时候被清理:栈空间里的变量一旦出了变量的作用域就会被系统回收;堆空间里的对象什么时候被回收:堆空间里面的对象,当没有任何变量指向它的时候就被标记为“垃圾”,表示可以被回收,被垃圾回收器(GC)来定时回收,标记为可回收的对象GC.Collect();//调用立即回收方法,一般不建议使用这个方法。
异常处理
            try
            {
                //...
            }
            catch (Exception ex)
            {
                throw;
            }
            finally
            {
 //此代码一定会执行,一般进行释放资源的操作
            }
局部变量与成员变量
局部变量:定义在方法里面的变量就叫做局部变量;没有默认的初始值,使用变量之前必须给它赋值。
成员变量:定义在类下面的变量叫做成员变量;如果是数值类型默认初始值为0 如果是引用类型默认初始值为空。
 软件设计中的几个原则:
    封装变化点
    开放封闭原则(对修改封闭,对扩展开放)---添加单独的功能—添加单独的方法;添加单独的角色---添加单独的类
    高内聚,低藕合  ---三层---EF   MVC
1.2    变量和常量
在程序执行过程中,其值不发生改变的量为常量,其值可变的量称为变量。
在程序中,常量是可以不经说明而直接引用的,而变量则必须先定义后使用。
变量
?    变量定义
[访问修饰符] 数据类型变量名 [=初始值];
注意:C#中不允许使用未初始化的变量
变量默认初始值:
任何数值类型的初始值:0(0.00)
string 的默认初始值:空字符串
object 的默认初始值:null
bool 的默认初始值:false
?    值类型与引用类型的变量
值类型赋值是拷贝
引用表示所使用的是变量对象的地址而不是变量或者对象本身;当声明引用类型变量时,程序只是分配了存放这个引用的存储空间;要想创建对象并把对象的存储地址赋给该变量,就需要使用New操作符;
例如: MyClass my; my = new MyClass();
当new操作符来创建对象是,C#会在堆空间为这个对象分配足够的空间来存放MyClass类对象的实例,然后把这个实例的地址赋给这个引用类型的 my;
例如:
int[] a = newint[3] { 1, 2, 3 };
for (int i = 0; i < 3; i++)
            {
Console.Write("{0}", a[i]);
            }
Console.WriteLine();
int[] b = a;
for (int i = 0; i < 3; i++)
            {
                b[i] = 2 * b[i];
            }
for (int i = 0; i < 3; i++)
            {
Console.Write("{0}", a[i]);
            }
Console.ReadKey();
//输出结果 123 246
//都是输出a[i],说明改变的是同一个内存中的数据,只不过引用变量不同,但指向的是同一个堆空间中的地址

常量
?    定义
常量的类型可以是任何一种值类型或引用类型。
?    直接常量
直接常量是指把程序中不变的量直接硬编码为数值或字符串值。
例如:
-100    //整型直接常量
"aaa"    //字符串型常量
true    //bool型常量
null    //对象引用常量
1.23e5    //浮点型直接常量
?    符号常量
符号常量是通过关键字const声明的变量,包括常量的名称和它的值。
const 数据类型常量名 = 初始值
注意:在程序中,常量只能被赋予初始值,一旦赋予一个常量初始值,这个常量的值在程序的运行中就不允许被改变,即无法对一个常量赋值。
C#编译器在编译的时候会将使用到常量的地方直接替换为常量的值,不会在程序集里面保留声明的常量名
1.3    装箱和拆箱
装箱转换
装箱转换是指将一个值类型的数据隐式地转换成一个对象类型的数据;把一个值类型装箱,就是创建了一个object类型的事例,并把该值类型的值复制给这个object实例。
例如:int i = 8; object obj = i;
在执行装箱转换时,也可以使用显示转换
例如:int i = 1; object obj = (object)i;
拆箱转换
拆箱转换是指将一个对象类型数据显式地转换成一个值类型数据。
拆箱分2步:首先检查对象的实例,确保它是给定值类型的一个装箱值,然后把实例的值复制到值类型数据中
例如:object obj = 2;int i = (int)obj;
注意:拆箱转换需要(而且必须)执行显示转换
1.4    形式参数和实际参数
?    函数的参数分为形参和实参两种。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。
?    形式参数(简称形参)是在用户自定义函数过程(Function)、子过程(Sub)过程名后圆括号中出现的变量名,多个形参用逗号分割。
?    实际参数(简称实参)是在调用上述过程时,在过程名后的参数,其作用是将它们的数据(值或地址)传送给被调过程对应的形参变量。
?    形参可以是:变量,带有一对括号的数组名。
?    实参可以是:同类型的常数、变量、数组元素、表达式、数组名(带有一对圆括号)。
?    函数的形参和实参具有以下特点:
?    形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。
?    实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。
?    实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。
?    函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
1.5    C#运算符
算术运算符
+ - * / % ++ --
字符串运算符
+
赋值运算符
=
+= 加赋值a+=b == a=a+b
-= 减赋值a-=b == a=a-b
*= 乘赋值a%=b == a=a*b
/= 除赋值a/=b == a=a/b
%= 取模赋值a%=b == a=a%b
<<= 左移赋值 a<<=b == a=a<<b
>>= 右移赋值 a>>=b == a=a>>b
&= 与赋值a&=b == a=a&b
^= 异或赋值 a^=b == a=a^b
|= 或赋值 a|=b == a=a|b
关系运算符
关系运算符是二元运算符,用于比较两个云算数,当结果为true或非0时,对应值为1,否则为0;
<,<= ,>,>= , == , !=
逻辑运算符
逻辑运算符用于bool值,结果为true或false
! 逻辑非 !(2<3) 返回false
&&逻辑与
|| 逻辑或
位运算符
位(bit)是计算机中表示信息最小的单位,一般用0和1表示.8个位组成一个字节;
位运算符的运算对象必须为整数;
~ 按位求反!2
<<左移 8<<2
>>右移 8>>2
&按位与8&5
| 按位或 8&5
条件运算符
条件运算符是一个3元运算符,每个操作数同时又是一个表达式的值;
表达式1 ? 表达式2 :表达式3
表达式1 为true 返回表达式2 ,否则返回表达式3
其他运算符
Is运算符
Is运算符用于检查表达式是否是制定的类型,如果是,其结果为true,否则结果为false
例如: double d = 1.23;
Bool t1 = d is int; //t1 = false
Bool t2 = d id double; //t2 = true;
Sizeof运算符
Sizeof运算符求值类型数据在内存中占用的字节数;
Sizeof(类型标识符)
Typeof运算符
该运算符用于获得制定数据类型的说明;
Console.Write(“{0}”,typeof(Student));
New运算符
该运算符用于创建一个类的实例
1.6    C#中常用类
String类MSDN
using System;
字符串是一个字符数组
字符串是一种特殊的引用类型,具有恒定性及不可变性
字符串是不可变的,当我们创建一个新的字符串对象的时候,首先会去字符串池里检查是否有同样的对象存在,如果有,直接指向这个对象,如果没有则创建1个新的对象
当我们修改1个字符串对象的时候,不是去修改这个字符串变量指向的对象,而是重新新建一个字符串对象将这个变量指向它
System.Text.StringBuilder当我们进行大批量的字符串拼接操作时建议使用StringBuilder用此对象效率更高(修改字符串而不创建新的对象,在System.Text类下)
字符串的本质就是一个字符数组
字符串的定义:string str=””    string str=new string(new char[]{});
字符是不可变的---字符串是静态文本值,如果对字符串值做修改,系统会重新为其分配一块空间,但是原有的空间也不会被收回,而是继续放在常量区,直到应用程序结束才收回
String类的常用属性
?    Chars: 获取当前 String 对象中位于指定字符位置的字符
?    Length: 获取当前 String 对象中的字符数
String类的常用方法及说明
?    Compare 静态方法;比较两个制定的String对象
?    CompareTo 非静态方法;将此字符串与制定的对象或String进行比较,并返回两者相对值的指示;
?    Concat 静态方法;连接String的一个或多个字符串
?    Contains 非静态方法;返回一个值,该值指示制定的String对象是否出现在此字符串中;
?    Equals 非静态方法;确定两个String对象是否具有相同的值;
?    Format 静态方法;将制定的String中的每个格式项替换为相应对象的值的文本等效项;
?    IndexOf 非静态方法;返回String或一个或多个字符在此字符串中的第一个匹配项的索引;
?    Insert 非静态方法;在该String中的制定索引位置插入一个制定的String
?    Remove 非静态方法;从该String中删除制定个数的字符;
?    Replace 非静态方法;将该String中的制定String的所有匹配项代替为其他制定的String
?    Split 非静态方法; 返回包含该String中的字符串(由制定Char或String数组的元素分隔)的String数组;
?    SubString 非静态方法;从此字符串中检索子字符串;
?    ToLower 非静态方法;返回该String转换为小写的副本
?    ToUpper 非静态方法;返回该String转换为大写的副本;
?    Trim 非静态方法;从此字符串的开始位置和末尾位置移除一组制定字符的所有匹配项
Math类MSDN
Math类位于System命名空间中,包含了常用的算术运算功能方法,这些都是静态方法,可通过”Math.方法名(参数)”使用
?    Abs        //绝对值
?    Ceiling        //返回大于或等于数字的最小整数
?    Floor        //返回小于或等于数子的最大整数
?    Max        //返回两个数中的较大的一个
?    Min        //返回两个数中的较小的一个
?    Sqrt        //返回制定数字的平方根
?    Truncate    //计算一个数字的整数部分
Convert类MSDN
Convert类位于System命名空间中,用于将一个值类型转换成另外一个值类型;这些方法都是静态方法;”Convert.方法名(参数)”使用
Convert不仅仅转换还是改造。
?    ToBoolean
?    ToDataTime
?    ToInt32
?    ToNumber        //将数据转换为Double类型
?    ToObject        //
?    ToString        //
Convert.ToInt32(false);//运行结果是0
Convert.ToInt32(true); //运行结果是1
Convert.ToInt32(null); //运行结果是0
DateTime结构MSDN
DateTime位于System命名空间中,DateTime值类型取值在公元00001-01-01 12:00:00 ~ 9999-12-31 23:59:59之间的日期和时间;
DateTime dt = new DateTime(年,月,日,时,分,秒);
DateTime结构的常用属性:
?    Date        //获取此实例的日期部分
?    Day        //获取此实例所表示的日期为该月中的第几天
?    DayOfWeek        //获取此实例所表示的日期是星期几
?    DayOfYear        //获取此实例所表示的日期是该年中的第几天
?    Hour        //获取此实例所表示日期的小时部分
?    Millisecond        //获取此实例所表示日期毫秒部分
?    Minute        //获取此实例所表示日期分钟部分
?    Month        //获取此实例所表示日期月份部分
?    Now        //获取一个DateTime对象,该对象设置为此计算机上的当前时间
?    Second        //获取此实例所表示日期的秒部分
?    TimeOfDay    //获取此实例的当天时间
?    Today        //获取当前日期
?    Year        //获取此实例所表示日期的年份部分
DateTime结构的常用方法:
?    AddDays        //非静态方法 将制定的天数添加到此实例的值上
?    AddHours        //非静态方法 将制定的小时添加到此实例的值上
?    AddMilliseconds    //非静态方法 将制定的毫秒添加到此实例的值上
?    AddMinutes        //非静态方法 将制定的分钟添加到此实例的值上
?    AddMonths        //非静态方法 将制定的月份添加到此实例的值上
?    AddSeconds        //非静态方法 将制定的秒数添加到此实例的值上
?    AddYears        //非静态方法 将制定的年份添加到此实例的值上
?    Compare        //静态方法 比较两个DateTime的实例,并返回它们相对值的指示
?    CompareTo        //非静态方法 将此实例与制定的对象或值类型进行比较,并返回二者相对值的指示
?    DaysInMonth    //静态方法 返回制定年和月中的天数
?    IsLeapYear        //静态方法 返回制定的年份是否为闰年的指示
?    Parse            //静态方法 将日期和时间的制定字符串表示转换成其等效的DateTime
/*
            string常用方法-Format
            string.Format("{0:d}",dt);//2005/11/5
            string.Format("{0:D}",dt);//2005年11月5日
            string.Format("{0:f}",dt);//2005年11月5日 14:23
            string.Format("{0:F}",dt);//2005年11月5日 14:23:23
            string.Format("{0:g}",dt);//2005-11-5 14:23
            string.Format("{0:G}",dt);//2005-11-5 14:23:23
            string.Format("{0:M}",dt);//11月5日
            string.Format("{0:R}",dt);//Sat, 05 Nov 2005 14:23:23 GMT
            string.Format("{0:s}",dt);//2005-11-05T14:23:23
            string.Format("{0:t}",dt);//14:23
            string.Format("{0:T}",dt);//14:23:23
            string.Format("{0:u}",dt);//2005-11-05 14:23:23Z
            string.Format("{0:U}",dt);//2005年11月5日 6:23:23
            string.Format("{0:Y}",dt);//2005年11月
            string.Format("{0}",dt);//2005-11-5 14:23:23
            string.Format("{0:yyyyMMddHHmmssffff}",dt);
            */
Console.WriteLine(string.Format("{0:yyyyMMddHHmmssffff}", DateTime.Now));
Console.WriteLine(DateTime.Now.ToString("yyyyMMddHHmmssffff"));
备注:yyyyMd可以去除0,例如:20140902->201492
1.7    C#控制语句
选择控制语句
?    If语句
If ( 条件表达式 ) 语句;
?    If … else 语句
If ( 条件表达式 )
    语句 1;
Else
    语句 2;
?    If … else if 语句
If ( 条件表达式式1 ) 语句 1;
Else if ( 条件表达式2 ) 语句 2;
…
Else if ( 条件表达式n )  语句 n;
Else 语句 n+1;
?    Switch语句
Switch ( 表达式 )
{
Case 常量表达式1:语句1;
    Case 常量表达式2:语句2;
    Case 常量表达式3:
    Case 常量表达式4:
        语句4;
        Break;
    …
    Case 常量表达式n:语句n;
    Default :语句n+1;
}
记得添加(Default也要添加)break ;
循环控制语句
?    While
While ( 条件表达式 ) 语句;
?    Do – while
Do 语句; while ( 条件表达式 );
?    For语句
For( 表达式1;表达式2;表达式3; ) 语句;
跳转语句
?    Break
Break 语句使程序从当前的循环语句(do while 和 for)内跳转出来,接着执行循环语句后面的语句;
?    Continue 语句
Continue 也用于循环语句,但它不是结束循环,而是结束循环语句的当前一次循环,接着执行下一次循环;
?    Goto语句(较少使用)
Goto语句可以跳出循环和switch语句;goto语句用于无条件转移程序的执行控制,它总是与一个标号相匹配;
Goto 标号;
“标号”是一个用户自定义标识符,它可以处于goto语句前面,也可以处于其后面.但是标号必须与goto语句处于同一个函数中.定义标号时,由一个标识符后面跟一个冒号组成;
?    示例
While(true)
{
    I++;
If (I > 10) goto end;
}
End :Console.Write(“OK”);
1.8    数组
所有存储结构中效率最高的,因类它在内在中是一串连续的空间
数组分为一维数组,二维数组,和三维数组,通常把二维数组称为矩阵,三维及以上的数组称为多维数组;
数组:同样一种类型的数据可以存放指定个数的一组数据,存储的数据称为元素。
数组声明时必须要指定数组的长度;数组里的每一个元素的数据类型都是一致的;数组的长度必须在声明时指定,一旦声明将不能被更改。
数组是所有存储结构最快的,因为它是连续的存储空间;
一维数组
数组类型[] 数组名;
?    一维数组的动态初始化
动态初始化需要借助new运算符,为数组元素分配内存空间,并为数组元素赋值,数组类型初始化为0,布尔类型初始化为false,字符串类型初始化为null;
动态初始化格式
数组类型[] 数组名= new 数组类型[n] { 元素0,元素1, … ,元素n-1 };
?    一维数组的静态初始化
静态初始化数组时,必须与数组定义结合在一起,否则会出错;
静态初始化格式
数据类型[] 数组名= { 元素0,元素1, … ,元素n-1 };
?    访问一维数组中的元素
For
Foreach
例如:
int[] arr=new int[5];            新定义一个数组长度为5(常用)
int[] arr1=new int[]{0,1,2,3,4};    定义一个数组,里面的元素为0,1,2,3,4
int[] arr2=new int[5]{0,1,2,3,4};定义一个数组,里面的元素为0,1,2,3,4 长度为5
int[] arr3={1,2,3,5,4};            不通过new创建数组(常用)
赋值:arr[0]=100;
取值:int x =取值通过arr[1];
int? count = 0;
int?[] intArr = newint?[] { 0, 1, 3, null, 5 };
for (int i = 0; i < intArr.Length; i++)
            {
if (intArr[i] != null) //对于引用类型象数组才有用(string) //int类型始终不等与int?,int类型始终不等于null
                {
                    count += intArr[i];
                }
            }
MessageBox.Show(count.ToString());

二维数组
数组类型[,] 数组名;
?    二维数组的动态初始化
数组类型[,] 数组名 = new 数据类型[m][n] {
{元素0, 0, 元素0,1, … , 元素0,n-1 }},
{元素1, 0, 元素1,1, … , 元素1,n-1 }},
…
{元素m-1, 0, 元素m-1,1, … , 元素m-1,n-1 }}
};
M,n分别为行数和列数
可以省略数组长度
Int[,] = new int[2,3]{{1,2,3},{4,5,6}};
Int[,] = new int[,]{{1,2,3},{4,5,6}};
Int[,] arr = new int[x,y];
x:表示行数
y:表示列数
for遍历也是先遍历行数,再遍历列数,GetLength(0),GetLength(1)
for和foreach都可以循环出数据
例如:
int[,] mm = newint[3, 4] {
{ 1, 2,3,4 },
{ 11, 12 ,13,14},
{ 111, 121,131,141 } };
for (int i = 0; i < mm.GetLength(0); i++)
            {
for (int j = 0; j < mm.GetLength(1); j++)
                {
Console.WriteLine(mm[i, j]);

                }
            }
foreach (int i in mm)
            {
Console.WriteLine(i);
            }

?    二维数组的静态初始化
数组类型[,] 数组名 = {
{元素0, 0, 元素0,1, … , 元素0,n-1 }},
{元素1, 0, 元素1,1, … , 元素1,n-1 }},
…
{元素m-1, 0, 元素m-1,1, … , 元素m-1,n-1 }}
};
例如:
//访问二维数组中的元素
int[,] arr2 = { { 1, 2, 3 }, { 4, 5, 6 } };
for (int i = 0; i < 2; i++)
            {
for (int j = 0; j < 3; j++)
                {
Console.Write("{0}", arr2[i, j]);
                }
            }
foreach (int i in arr2)
            {
Console.Write("{0}", i);
            }
Console.WriteLine();
//arr2.GetLength(int index);    //得到指定维度的长度;
for (int i = 0; i < arr2.GetLength(0); i++)
            {
for (int j = 0; j < arr2.GetLength(1); j++)
                {
Console.Write("{0}", arr2[i, j]);
                }
            }
Console.WriteLine("取值{0},{1};维度{2}", arr2[1, 1], arr2.GetValue(1, 1), arr2.Rank);
Console.ReadKey();
//输出结果
//123456123456
//123456取值5,5;维度2

int? count0 = 0;
int? count1 = 0;
int?[] intArr = newint?[] { 0, 1, 3, null, 5 };
for (int i = 0; i < intArr.Length; i++)
            {
if (intArr[i] != null) //对于引用类型象数组才有用(string) //int类型始终不等与int?,int类型始终不等于null
                {
                    count0 += intArr[i];
                }
            }
foreach (object var in intArr)
            {
if (var != null)
                {
                    count1 += int.Parse(var.ToString());
                }
            }
MessageBox.Show("for嵌套循环==foreach:" + count0.ToString() + "," + count1.ToString());

 交错数组
交错数组是元素为数组的数组,与多维数组类似,所不同的是,交错数组元素的维度和大小可以不同,而多维数组的均相同
交错数组的本质就是一个一维数组,只不过这个数组里面的每一个元素值又不一个数组
交错数组的元素需要先实例化,因为数组是一个引用类型的变量,需要先实例化再使用,如果没有实例化会出现 “未将对象引用设置到对象的实例”
因为交错数组的每一个元素类型是数组,所以遍历的时候就可以按遍历一维数组的方式一考虑

示例
Int[][] arr = new int[3][];
必须初始化才能使用
Arr[0] = new int[5];
Arr[1] = new int[4];
也可以填充数据
Arr[0] = new int [3] {1,2,3};
也可以在数组生命时初始化
Int[][] arr = {
    New int[]{1,2,3},
New int[]{4,4,5}
};
Int[][] arr = new int[][]{
    New int[]{1,2,3},
New int[]{4,4,5}
};
int[][] arr = newint[2][];
//数组的每个元素又是一个数组对象,所以需要先实例化数组对象
            arr[0] = newint[] { 1, 2, 3 };
            arr[1] = newint[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (int i = 0; i < arr.Length; i++)
            {
for (int j = 0; j < arr[i].Length; j++)
                {
//Console.Write(arr[i][j] + "");
MessageBox.Show(arr[i][j] + ",");
                }
            }

?    访问交错数组
For嵌套查询
1.9    集合
Array 类
Array类是所有数组类型的抽象基类型,它提供了创建,操作,搜索和排序数组的方法.在C#中数组实际上是对象;
Array类的属性个和方法
?    常用属性
Length
LongLength
Rank        //获取Array的秩(维数)
?    常用方法
BinarySearch
Copy
CopyTo
Find
ForEach
GetLength
GetLongLength
GetLowerBound
GetUpperBound
GetValue
IndexOf
Resize
Reverse
SetValue
Sort
Array 类中方法的使用
ArrayList 类
该类位于命名空间Syestem.Collections中,用于建立不定长度的数组,由于该类数组的数据类型为Object,因此在数组中的元素可以是任何类型的数据;由于其长度不固定,可以将其对象看成是一个集合;
ArrayList 数组名= new ArratList();    //可以看成是一个动态数组
?    ArratList类的属性
Capacity        //获取或设置ArrayList可包含的元素数
Count            //获取ArrayList中实际包含的元素数
Item            //获取或设置指定索引处的元素
?    ArrayList类的方法
Add
AddRange
BinarySearch
Clear
Clone
Contains
Copyto
GetRange
InsexOf
Inset
InsetRange
LastIndexOf
Remove
SetRange
Sort
ToArray
ToString
TrimToSize
?    ArrayList 存储原理:
存储数据的是1个object类型的数组,这个对象初始化的时候,这个数组的长度为0;
当我们往其中新增一个元素的时候,会将这个数组的长度变为4,然后将元素加入到数组里面,当进行其他次的添加时,如果数组满了会新建一个数组长度是原来数组的2倍。
查询:foreach for遍历
不足:
1.如果存储值类型,需要装箱和拆箱操作
2.不管什么类型的数据,存储之后,取出来使用必须通过类型转换
List<T>类
List<T>类提供用于对列表进行搜索,排序和操作的方法.该类的定义放在命名空间System.Collections.Generc中,其中T为列表中元素的类型
List<T>类体ArrayList类的泛型等效类,该类使用大小可按需动态添加的数组实现IList泛型接口
List<T>数组名= new List<T>();
?    List<T>的属性
Capactity    //获取或这是该内部数据结构在不调整大小的情况下能够保存元素总数
Count        //获取List中实际包含的元素数
Item        //获取或设置指定索引处的元素
?    List<T>的方法
Add
AddRange
BinarySearch
Clear
Clone
Contains
Copyto
Exists
Find
FindAll
FindIndex
FindLastIndex
ForEach
InsexOf
Inset
InserRange
LastIndexOf
Remove
RemoveAll
RemoveAt
RemoveRange
Reverse
Sort
ToArray
TrimExcess
int[] nums = newint[] { 3535, 5, 34, 534234324, 32, 5435, 3, 423, 4, 54 };
List<int> list = newList<int>();
for (int i = 0; i < nums.Length; i++)
            {
//if (nums[i] % 2 != 0)
//{
//    list.Add(nums[i]);
//}
                list.Add(nums[i]);
            }
            list.Sort();
for (int i = 0; i < list.Count; i++)
            {
Console.WriteLine(list[i]);
            }
Console.Read();

Hashtable
1)    在collections类下
using System.Collections;
Hashtable table=new Hashtable();//哈希表 键值对 键和值都是Object类的
table.Add("name","LiuDeHua");//新增 键不可以重复
table["name"]="LiMing";//修改
table.Add=("person",new Person());//取值 根据键取值
Person p= table["person"] as Person; //放进去是一个object类,取出来时也是一个object,需要进行类型转换
table.Remove("person"); //删除 根据键删除
//遍历HashTable的两种方式
object obj=table["Person"];
foreach(string key in table.Keys)//键的集合
{
Console.WriteLine(table[key]);
}
foreach(string value in table.Values)//值的集合
{
Console.WriteLine(table[value]);
}
2)    哈希表存储数据的原理:哈希是一种算法
哈希表存储数据是一个数组 backet结构数组(key,value,hase_coll),输入key之后根据一定的算法(哈希算法)算出key值应该存储的下标
3)    哈希表的某些方法:
table.ContainsKey();//返回布尔值
4)    什么时候用?
需要频繁的取单个值,则可以选用hashtable;
若是只需要存进去,能遍历就可以用集合
泛型集合
List<T>
Dictionary<Tkey,TValue>
using System.Collections.Generic;
List<T>类似于ArrayList—长度动态变化的数组
Dictionary<K,V>类似于Hashtable
推荐使用泛型集合?原因????
T,K,V就像一把锁,锁住集合只能存某种特定的类型,这里的T,K,V也可以是其它字母
泛型集合可以进行foreach遍历,是因为实现了IEnumerable<T>具有了GetEnumerator()方法
先看foreach的本质

<这里写指定的数据类型(包括类)>,尖括号里面指定是什么类型就要存入什么类型
List<Person> list=new List<Person>();//对应arraylist
增加:list.Add(i);
取值:list[1];
遍历:foreach(Person p in list){}
哈希值的泛型集合:
Dictionary<string,string> dic=new Dictionary< string,string> //对应哈希值
dic["name"]; 取值
三种遍历方式:
foreach(string key in dic.Keys) //以键遍历
{
console.WriteLine(dic[key]);
}
foreach(string value in dic.Values) //以值遍历
{
console.WriteLine(dic[value]);
}
foreach(KeyValuePair< string ,Person> kvp in dic)//以键值对来遍历 //遍历哈希表中的所有键值对
{
console.WriteLine(kvp);
}
List<T>:相当于一个长度动态变化的数组:长度任意扩展,但是有数组类型规范
命名空间:using System.Collections.Generic;
创建:List<T> lists=new List<T>();T是指用户自定义的类型,可以是任意的类型,如 List<int> list=new List<int>();说明这个集合里面只能存放整型类型的数据
通过索引删除和访问及修改
Dictionary<key,value>补充:
Dictionary<key,value>:相当于规范了类型的hashTable
定义:Dictionary<string, string> dics = new Dictionary<string, string>();
添加元素:只能通过Add(),如:
  dics.Add("aa", "aa");
            dics.Add("bb", "bb");
删除:通过key值删除
遍历:三种遍历方法:遍历key,遍历value,通过类型DictionaryEntry遍历
使用总结
1.    Arraylist与List<T>:本质上来说,存储数据是就是object[]
a)    长度是可变的  length:长度 capacity:容量,capacity不能小于实际内容的值,当你将length变大之后,capacity会自动变大。
b)    它的类型是object,写入和读取的时候需要做装箱和折箱
c)    增加:Add(object)
d)    删除:Remove(object)  RemoveAt(int index)
e)    集合通过索引下标访问
f)    clear();清空
g)    Contains():判断是否包含某一个元素
h)    ToArray();
不足:
?    如果存储值类型,需要装箱和拆箱操作
?    不管什么类型的数据,存储之后,取出来使用必须通过类型转换

1.    HashTable与Dictionary<Key,Value>
a)    通过Add()方法添加元素,需要同时添加key和value
b)    通过key访问和删除
c)    数据其实是存放于一个burket[]中的
key
value
hash_coll:它是由key通过算法得到,用来标明数据存储的地址空间,在遍历的时候通过这个hash_coll值取出对应的value,所以它的存取效率非常高,因为不需要像arrlist做遍历
2.    有三种遍历方式:

        foreach(object obj in ht.Keys)
    {
    Console.WriteLine(obj);
     }
        --------------------------------------------
        foreach (object obj in ht.Values)
    {
    Console.WriteLine(obj);
     }
    ---.----------------------------------------------
       foreach (DictionaryEntry obj in ht)
         {
            Console.WriteLine(obj.key +”  :  “+obj.value);
         }
    foreach (KeyValuePair<Tkey,Tvalue> obj in ht)
         {
            Console.WriteLine(obj.key +”  :  “+obj.value);
         }
不足:1.如果存储值类型,需要装箱和拆箱操作  2.不管什么类型的数据,存储之后,取出来使用必须通过类型转换

所以可以这样认为:
List<T>和Dictionary<key,value>规范了类型,在做遍历的时候不需要做类型转换,同时也了数据安全。
    1.list<T>与ArrayList:使用索引访问和删除,也可能通过对象删除,使用Add进行对象的添加,也可以通过AddRange()一次性添加多个元素
    2.HashTable与Dictionary<key,value>:通过key值访问和删除,遍历的时候可以选择遍历key或者遍历value.在添加元素的时候需要添加key值和value值
1.10     Foreach遍历原理
1)    实现了IEnumerable接口,接口里面定义了GetEnumerator的方法,这个要求返回实现了IEnumerator(迭代器)的对象
IEnumerator tor=list.GetEnumrator();//枚举器 遍历器 迭代器
while(tor.MoveNext()) //调用迭代器的MoveNext方法(将当前指针往下移一位)判断其返回值
{
Console.WriteLine(tor.Current);//其返回值为true则调用迭代器的Current属性
}
p=new Person(){Name="张三"}
2)    foreach(person p in pc)
第一步: IEnumerable able=p as IEnumerable; //转换为一个迭代器)
第二步: IEnumerator tor=able.GetEnumerator();//调用迭代器的GetEnumerator()方法,返回一个IEnumerator对象
第三步: while(tor.MoveNext()) //调用IEnumerator对象的MoveNext方法(将当前指针往下移一位)判断其返回值
{
Console.WriteLine(tor.Cunrrent);//其返回值为true则调用迭代器的Current属性
}
时间: 2024-10-15 01:05:28

C#程序设计教程 - 第1章 C#程序设计基础的相关文章

[WuDe]C#程序设计教程 - 第1章 C#程序设计基础

第1章 C#程序设计基础 1.1 C#基础 概述 .NET是一个平台 C#是一门语言 编译原理 1) 编写源代码 2) C#编译器将C#源代码编译成MSIL 3) 将中间代码交给CLR的即时编译器(JIT)将微软中间语言转化成相应的机器码(CPU指令) 4) 交给CPU运行机器码 被CLR所管理的代码叫做托管代码,不能够被CLR管理的代码叫做非托管代码,非托管代码运行效率高,但是相对安全性较低;托管代码运行效率较低,但代码相对是安全的. 标识符 以字母/下划线/数字组成,且第一个字符必须为字母或

C#程序设计教程 – 第1章 C#程序设计基础

C#程序设计教程 作者:WuDe???????? 时间:20140626???? C#程序设计基础 C#基础 概述 .NET是一个平台 C#是一门语言 编译原理 编写源代码 C#编译器将C#源代码编译成MSIL 将中间代码交给CLR的即时编译器(JIT)将微软中间语言转化成相应的机器码(CPU指令) 交给CPU运行机器码 被CLR所管理的代码叫做托管代码,不能够被CLR管理的代码叫做非托管代码,非托管代码运行效率高,但是相对安全性较低;托管代码运行效率较低,但代码相对是安全的. 标识符 以字母/

汇编语言程序设计读书笔记(4)- 程序设计基础之一

目录: 一.数据定义 1.变量数据定义 2.常量数据定义 3.缓冲区定义 二.寻址方式 1.立即数寻址 2.寄存器寻址 3.直接寻址 4.寄存器间接寻址 5.寄存器相对寻址 6.变址寻址 三.数据传送和mov指令 1.数据传送规则 2.mov指令 四.条件传送数据cmov指令 1.状态标志位 2.cmov指令 五.交换数据 1.xchg指令 2.bswap指令 3.xadd指令 4.cmpxchg指令 5.cmpxchg8b指令 六.堆栈 1.堆栈简介 2.入栈指令push 3.出栈指令pop

[WuDe]C#程序设计教程 - 第2章 C#面向对象基础

第2章 C#面向对象基础 2.1 类 类是一种数据类型,而对象是具有这种类型的变量 [类的修饰符] class 类名 [:基类名] { //类的成员 }[;] 访问级别的用处在于控制成员在哪些地方可以被访问,这样达到面向对象中"封装"的目的;控制对外访问权限 在类这个级别,不写访问修饰符默认为internal,类只有两个访问修饰符,public 和internal (暂时这样理解),在类里面,方法外定义变量或方法前不加访问修饰符,默认为private Public公共类:不受限制对该类

Java程序设计教程-第1章绪论

1.谁是Java语言的创始人? Games Gosling 2.Java语言有哪些优点和缺点? Java语言的特点:(1)简单性,Java去掉了C++中的很多复杂的机制;(2) 网络特性,Java语言因为对互联网络的良好支持而得到迅速推广的;(3)面向对象,彻底的面向对象特性;(4)平台无关性/可移植性,Java语言是解决异构平台兼容性解决的最好的语言之一;(5)鲁棒性;(6)安全性,网络上运行的Java程序是符合网络安全协议的;(7)多线程性,多线程是提高程序运行效率的一种方法;(8)解释性,

第2章 C#程序设计基础

一.填空题 1. C#每条语句以   “:”  字符结尾. 2. C#提供了两种注释方法:单行注释和  多行注释  . 3. C#值类型包括简单类型.  结构类型  和枚举类型. 4. 实数在C#中采用两种数据类型来表示:  单精度  和双精度. 5. C#中提供了  隐式转换  和显式转换两种转换类型. 6. C#中提供了三种程序控制语句:顺序语句.  选择语句   和  循环语句  . 7. 面向对象的三大特性为:封装性.继承性和  多态性  . 8. C#中类的方法可以分为三种类型:无返回

iOS游戏框架Sprite Kit基础教程第1章编写第一个Sprite Kit程序

iOS游戏框架Sprite Kit基础教程第1章编写第一个Sprite Kit程序 程序是为了实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合.本章将以编写第一个Sprite Kit程序为主线,为开发者讲解什么是Sprite Kit.苹果账号的注册.Xcode的下载和安装.编写程序.调试等内容.选自iOS游戏框架Sprite Kit基础教程Swift版上册大学霸 1.1  Sprite Kit介绍 从iOS 7开始添加了Sprite Kit.本节将为开发者讲解什么是Sprite Ki

第三章—Windows程序

这一章我都不知道该如何写了,呵呵~~ 毕竟,Win32是一个非常深奥的系统,目前还容不得我这种 小辈在这儿说三道四,不过,我既然是要写给那些入门阶段的朋友们看的,又不是写给那些搞程序设计老鸟看的,所以,我也犯不着怕被人背后指着骂 本章的名字就叫<Windows程序>而不是<Windows程序设计>所以,我只是讲一些关于Windows程序运作的原理: Windows 为什么叫Windows,相信所有用过的朋友都可以明白,那桌面上一个一个的窗口,就是它名字的由来.也就是这一个又一个窗口

《80X86汇编语言程序设计教程》二十五 结语(读后感:这本书怎么样)

这本书的推荐星级是:5星.毕竟是经典书籍,没什么好说的. 就汇编本身而言,在编写高效率程序以及对程序的优化,调试,工程的逆向都是一门基础:就理论上的操作系统而言,汇编让你了解CPU,了解计算机的体系结构,它是阅读操作系统源码的前提,这也是<80X86汇编语言程序设计教程>做得比较好的一点,它对386的保护方式下的编程写得比较详实,读完整本书,会发现这学的不仅仅是汇编语言,还有CPU的体系架构,它让你基本猜测得到在编写基于80386CPU的操作系统时,大概要做一些什么事情. 阅读前,我选过几本书