第二讲 C#基础
为适应未来社会的发展,微软推出了.NET战略,以此作为下一代网络应用基础,微软的所有产品,包括操作系统、办公软件以及开发工具都将转移到.NET平台上。.NET所包含的技术全面先进,必将对今后的软件开发和应用形式产生巨大的影响。
在.NET平台上,C#是微软为了适应未来网络技术的发展而推出的新一代编程语言,它是专为.NET战略开发的编程语言。
虽然.NET平台上包含多种编程语言,并且所有语言开发出的.NET应用程序效果也是一样的,但是,C#本身作为一种编程语言,它是从著名的C++ 进化而来的,并综合了C++ 和Java的优点,是.NET主推的语言,在.NET中起着不可或缺的作用,有志于在.NET平台上开发的程序员必须了解这种语言。
第一节 C#程序结构:
我们先来做一个最简单的控制台程序,并以此作为下一步试验的基础。
using System;
namespace cs1
{
class Class1
{
//应用程序的主入口点。
[STAThread]
static void Main(string[] args)
{
}
}
}
程序结构结构:
using System;
指明本程序引用的名称空间,后面需要用到的类,都要事先using进来。
namespace 语句
定义一个“语句”名称空间,这个程序定义的所有类,都位于这个名称空间下面,一个程序可以定义多个名称空间,一个名称空间可以包含多个类。但一个类只能处于一个名称空间下面。不同名称空间下相同名字的类,被认为是完全不同的东西。
static void Main(string[] args)
{
}
这是应用程序主入口点,用作方法的返回类型时,void 关键字指定方法不返回值。在方法的参数列表中不允许使用 void。例如,采用以下形式声明一个无参数的、不返回值的方法:void MyMethod();
我们的实验程序可以写到这个方法里面,比如:
static void Main(string[] args)
{
Console.WriteLine("您好,这是我的第一个C#程序");
}
运行:
当然,后面我们也会讨论真正的Windows Form程序的写法,不过,控制台程序作为一个基础,你是必须要掌握的。
第二节 数据类型和运算符
一、 常数
在C#语言中,有布尔型常数,数值型常数和数值型常数。
2-1布尔型常数
布尔型常数只有true和false两个值,它们也是关键字。实际上在C语言中是没有这两个常数的,在那里是用0表示false,而其他非0数值表示true。但是在C#里面为了减少错误,还是加入了这两个常数,以保证语言的确定性。使用这个常数的更重要的原因,是.NET平台上不同语言布尔类型的数值并不相同,为了不同语言之间便于转换,请坚持使用true/false表示布尔类型的结果,这样利用中间语言,就可以实现不同语言之间的正确转换。
2-2数值型常数
数值型常数分为整形常数和浮点型常数。
整形常数:
十进制表示:2,100等;
十六进制表示(前面加0x):0x10(相当于十进制16);
这个区别要加以注意。
浮点型常数:
比如:1.25,-1.23 ,0.884, .884;
也可以用科学计数法:25e20(表示20乘以10的20次方)。
试验:
Console.WriteLine(10);
Console.WriteLine(0x10);
Console.WriteLine(1.884);
Console.WriteLine(2.4e25);
结果:
10
16
1.884
2.4E25
注意:在java里面,010表示八进制的8,但在C#中间并没有这种表示,它仍然是十进制的10,这些区别要加以注意。
字符型常数
字符型常数包括单个字符和一串字符,也可以是不可打印的字符,如下表:
特殊字符 |
说明 |
\ |
续行符,表示下一行是本行的继续 |
\n |
换行符 |
\b |
退格 |
\r |
回车 |
\f |
走纸 |
\t |
水平制表符 |
\v |
垂直制表符 |
\\ |
反斜线 |
\’ |
单引号 |
\’’ |
双引号 |
\0 |
空字符,即字符0 |
下面是一些合法的字符型常数:
‘A’,’B’,’\n’,’t’,’\\’
二、 标示符
在C#中,标示符的第一个字母必须是下列之一:大小写英文字母,下划线(_),美元符($),其它都是错误的。
理论上,标示符的长度不受限制,但如果给类起名子,由于类是放在文件中的,所以名字要符合所用操作系统的规范。
定义变量的方法如下:
数据类型 标示符[,标示符……];
有些数据类型的变量,可以通过下面的方法转换数据类型
(数据类型)标示符
与C语言不同,变量一旦被声明,就会给它赋于初值,不同的变量初值是不同的。
第三节 基本数据类型和运算符
3-1 布尔型
布尔型用来处理逻辑判断,两目的布尔运算符见下表:
操作数1 |
操作数2 |
&(AND) |
&&(AND) |
|(OR) |
||(OR) |
^(XOR) |
True |
true |
true |
true |
true |
True |
false |
True |
false |
false |
False |
true |
true |
True |
False |
true |
false |
False |
true |
true |
True |
False |
false |
false |
false |
false |
false |
false |
单目运算符
操作数 |
!(NOT) |
True |
false |
False |
True |
要注意,&和&& 以及 |和||是有区别的:
&、|、^是位运算符,用于对两个整数进行位运算,它们同时也可以作为逻辑运算符,这点和C语言是一样的,但这点也值得探讨,因为C语言中逻辑真和假本来就是数字(1,0),当然可以进行位运算,但在java中,true和false是不能转换成数字的,所以用于逻辑运算从道理上就有点勉强了。
另外,还要看到位运算符和逻辑运算符在运算方式上也是不同的。
我们来看两个表达式:
(1) true||false
(2) true|false
在计算(1)的时候,系统只要看到true,就会计算出结果true,后面的部分就不管了。
而在计算(2)的时候,系统的确将true(看作1)和false(看作0)作了二进制计算,得到的是计算结果。
下面看一个例子:
int a=1;
int b=2;
Boolean c=true;
Boolean d=false;
Console.WriteLine(a|b);
Console.WriteLine(c|d);
Console.WriteLine(c||d);
结果:
3
true
true
第一个结果是3的原因在于:
01
or 10
-----------
11
这恰恰是十进制的3。
而如果写Console.WriteLine(c||d);将是错误的。
3-2 字符型
在C#中规定,字符型为16位Unicode编码(C中为8位),字符型可以通过转换转成整数型或浮点型,实际上,可以把字符型变成十六位无符号整数使用,整数的值是该字符Unicode编码。
字符型变量用char关键字声明,默认值为’\0’。
下面看一个例子:
char va='a',vb='b';
Console.WriteLine(va);
Console.WriteLine(vb);
Console.WriteLine("String:"+va+vb);
Console.WriteLine(va+vb);
Console.WriteLine((int)va);
Console.WriteLine((int)vb);
Console.WriteLine(va==97);
结果应该是:
a
b
String:ab
195
97
98
true
第三行:自动转成字符串连接;
第四行:作加法运算的时候,系统自动把它转成整形数(Unicode编码),用其和输出;
第五、六行:输出的是其Unicode编码;
第八行:进一步验证了字符数据和对应的编码之间的关系。
3-3整形
下表显示了整型的大小和范围,这些类型构成了简单类型的一个子集。
类型 范围 大小
sbyte -128 到 127 有符号 8 位整数
byte 0 到 255 无符号 8 位整数
char U+0000 到 U+ffff 16 位 Unicode 字符
short -32,768 到 32,767 有符号 16 位整数
ushort 0 到 65,535 无符号 16 位整数
int -2,147,483,648 到 2,147,483,647 有符号 32 位整数
uint 0 到 4,294,967,295 无符号 32 位整数
long -9,223,372,036,854,775,808
到 9,223,372,036,854,775,807 有符号 64 位整数
ulong 0
到 18,446,744,073,709,551,615 无符号 64 位整数
备注
如果整数表示的值超出了 ulong 的范围,将产生编译错误。
整形数据可以转成字符型和浮点型,一种整数型也可以转成另一种整数型,不过要注意,当长度大的向长度小的整数型转换的时候,可能丢失精度。
比如:
int i=350000;
short j=(short)i;
Console.WriteLine(i);
Console.WriteLine(j);
结果:
350000
22320
可以对整形数据进行操作的运算符有:
名称 |
运算符 |
进行运算的数据类型 |
运算结果类型 |
关系(双目) |
<,<= ,= = ,!= ,>=, > |
整形或者浮点型 |
布尔型 |
正负(单目) |
+ , - |
整形或者浮点型 |
与操作数一致 |
四则运算(双目) |
+,-,*,/,%(取模) |
整形或者浮点型 |
两个操作数都是整形时,结果为整形;有一个为浮点型是结果为浮点型。 |
增量和减量(单目) |
++ ,-- |
整形或者浮点型 |
与操作数一致 |
位移(双目) |
<<(左移一位),
>>(右移一位) |
整形 |
整形 |
位自反(单目) |
~ |
整形 |
整形 |
位运算(双目) |
&(AND),|(OR),^(XOR) |
整形 |
整形 |
当两个不同类型的操作数进行运算的时候,运算结果以较长位数的操作数为准。
下面是运算符的几个例子。
例一:
移位运算实际上是乘2或者除以2,而V 1<<V2,相当于V1*(2的V2次方);
V 1>>V2,相当于V1/(2的V2次方);
移位运算比实际的乘除速度要快得多。
下面的例子可以看到这个情况。
Console.WriteLine(1024<<2);
Console.WriteLine(1024>>2);
结果是
4096
256
例二:增量运算
int k=10,l=10;
Console.WriteLine(k);
Console.WriteLine(l);
k++;
l++;
Console.WriteLine(k);
Console.WriteLine(l);
结果:
10
10
11
11
例三:除数为0的错误
int izero,g;
izero=0;
g=1/izero;
结果为:
3-4 浮点型
C#有两种浮点类型:
float 32位
double 64位
类型 大致范围 精度
float ±1.5 × E-45 到 ±3.4 × E+38 7 位
double ±5.0 × E-324 到 ±1.7 × E+308 15 到 16 位
把浮点数转换成整数的时候,总是转换成接近0的那个整数。
Console.WriteLine((int)5.7);
Console.WriteLine((int)5.1);
Console.WriteLine((int)-5.7);
Console.WriteLine((int)-5.1);
结果为:
5
5
-5
-5
在使用浮点常数的时候,要注意一个问题:浮点常数总是作为double存放的,这和整数常数不同,所以,编译下面的例子就会出错:
float f;
f=3.56;
Console.WriteLine(f);
解决的办法,就是把浮点数强制转换为float类型。3.56f或者3.56F。
float f;
f=3.56F;
Console.WriteLine (f);
结果是:
3.56
货币类型:
Decimal 16 -79228162514264337593543950335 0.0000000000000000000000000001
到 到
0.0000000000000000000000000001 79228162514264337593543950335
保留四位小数。
在C#中,对声明变量是赋了初值的:
基本数据类型 初值
布尔型 false
字符型 ‘\0’
整形 0
float 0.0f
double 0.0d
3-6 运算符小结:
算术运算(二元):
+(加), -(减),*(乘),/(除), %(取模,求余数);
逻辑(布尔型和按位):
&(与) |(或) ^(异或) !(非) ~(求反) &&(逻辑与) ||(逻辑或)
true 和 false
其中,前五个操作符(& | ^ ! ~)也可以用于位操作。
字符串串联 +
递增、递减 ++ --
对操作数进行加一和减一操作,例如: x++,x--,++x,--x
移位 << >>
把操作数(int或long)移动指定的位数,其中<<左移,>>右移。
例如:
x<<1 (左移一位,相当于乘以2 )
x>>2 (右移两位,相当于除以4 )
注意,当进行左移操作的时候,被移出的高位将被忽略,低位将被填上0。
当进行右移操作的时候,低位移出部分将被忽略,如果操作的是有符号数
(int或long),则高位填上符号位,如果是无符号数,则高位将填上0。
关系(进行表达式的比较操作)
==(等于) !=(不等于) <(小于) >(大于)
<=(小于等于) >=(大于等于)
赋值
(X += 100 相当于 X=X+100 )
=(等于) +=(加等) -=(减等) *=(乘等) /=(除等)
%=(模等) &=(与等) |=(或等) ^=(异或等)
<<=(左移等) >>=(右移等)
成员访问 .(用来访问类的成员)
索引 []
利用索引的方法访问数组、索引器或属性。
例如:
int[] arr, arr[0]=1;
转换 ()
用来进行类型转换操作
例如:
int a=(int)X 把x转换成整数后赋给a。
条件 ?:
这是一个三元操作符,这个操作符根据表达式的值返回两个值中的一个,
例如:
int a=(x>0)?1:0
表示,如果x>0(true)则a=1,否则的话,a=0
创建对象 New
3-6 运算的优先级
算符的优先级和顺序关联性当一个表达式包含多个运算符时,这些运算符的优先级控制各运算符的计算顺序。
例如,表达式 x + y * z 按 x + (y * z) 计算,因为 * 运算符具有的优先级比 + 运算符高。运算符的优先级由运算符的关联语法产生式的定义确定。例如,一个增量表达式由以 + 或 - 运算符分隔的乘法表达式组成,因此给 + 和 - 运算符赋予的优先级比 *、/ 和 % 运算符低。
下表按照从最高到最低的优先级顺序概括了所有的运算符:
初等项 x.y f(x) a[x] x++ x-- new
typeof checked unchecked
一元 + - ! ~ ++x --x (T)x
乘法 * / %
加法 + -
移位 << >>
关系和类型检测 < > <= >= is as
相等 == !=
逻辑 AND &
逻辑 XOR ^
逻辑 OR |
条件 AND &&
条件 OR ||
条件 ?:
赋值 = *= /= %= += -= <<= >>= &= ^= |=
当操作数出现在具有相同优先级的两个运算符之间时,运算符的顺序关联性控制运算的执行顺序:
除了赋值运算符外,所有的二元运算符都向左顺序关联,意思是从左;
向右执行运算。例如,x + y + z 按 (x + y) + z 计算;
赋值运算符和条件运算符 (?:) 都向右顺序关联,意思是从右向左执;
行运算。例如,x = y = z 按 x = (y = z) 计算。
优先级和顺序关联性都可以用括号控制。例如,x + y * z 先将 y 乘以 z 然后将结果与 x 相加,而 (x + y) * z 先将 x 与 y 相加,然后再将结果乘以 z。
第四节 C#的表达式
4-1 条件选择语句
if-else
if 语句是表达式求得 true 值时执行代码块的控制语句。它的形式为:
if (expression)
statement1
[else
statement2]
此处:
expression
一个表达式,可隐式转换为 bool 或包含重载 true 和 false 运算符的类型。
statement1
expression 为 true 时执行的嵌入语句。
statement2
expression 为 false 时执行的嵌入语句。
备注
如果 expression 为 true,则执行 statement1。如果可选的 else 子句存在并且 expression 求得 false 值,则执行 statement2。执行 if 语句之后,控制传递给下一个语句。
如果 if 语句的两个结果中有任何一个结果(true 或 false)导致执行一个以上的语句,则可通过将多个语句包括在块中按条件执行它们。
在测试条件时执行的语句可以是任何种类的,包括嵌套在原始 if 语句中的另一个 if 语句。在嵌套的 if 语句中,else 子句属于没有相应 else 的最后一个 if。例如:
private void button2_Click(object sender, System.EventArgs e)
{
int x;
int y;
string z;
z=textBox1.Text;
x=20;
y=Int32.Parse(z);
if (x > 10)
if (y > 20)
textBox2.Text="这是第一个";
else
textBox2.Text="这是第二个";
}
注意:和VB.NET不同,C#中的等号是不能进行类型转换的,所以必须有下面的语句进行类型转换。
y=Int32.Parse(z);
在此例中,如果条件 (y > 20) 求得 false 值,则显示“这是第二个”。但如果要使 “这是第二个”与条件 (x >10) 关联,则使用大括号:
private void button3_Click(object sender, System.EventArgs e)
{
int x;
int y;
string z;
z=textBox1.Text;
x=8;
y=Int32.Parse(z);
if (x > 10)
{if (y > 20)
textBox2.Text="这是第一个";}
else
textBox2.Text="这是第二个";
}
在这种情况下,条件 (x > 10) 求得 false 值时将显示“这是第二个”。
例二:
第二个例子,可以体会一下“类”在这里的应用。先做一个类(可以放在最下面,也可以专门做一个类的文件)
public class IfTest
{
public System.Windows.Forms.TextBox textB;
public void Mychar(string c)
{
char s;
s=c[0];
if (Char.IsLetter(s))
textB.Text="这是字母";
else
textB.Text="这不是字母";
}
}
在Form1中,写Button事件:
private void button4_Click(object sender, System.EventArgs e)
{
IfTest Myclass;
Myclass=new IfTest();
string s;
s=textBox1.Text;
Myclass.textB=textBox2;
Myclass.Mychar(s);
}
这里,我们可以看到如何构造一个类的实例,并且通过引用传递,把类中的数据在textBox2种显示出来。
这里用到了字符类型,下面简要的说明一下:
char 类型的常数可以写成字符、十六进制换码序列或 Unicode 表示形式。您也可以显式转换整数字符代码。以下所有语句均声明了一个 char 变量并用字符 X 将其初始化:
char MyChar = 'X'; // Character literal
char MyChar = '\x0058'; // Hexadecimal
char MyChar = (char)88; // Cast from integral type
char MyChar = '\u0058'; // Unicode
转换
char 类型可隐式转换为 ushort、int、uint、long、ulong、float、double 或 decimal 类型。但是,不存在从其他类型到 char 类型的隐式转换。
关于New的问题,后面我们还会进一步讨论。
还可以扩展 if 语句,使用下列 else-if 排列来处理多个条件:
if (Condition_1)
Statement_1;
else if (Condition_2)
Statement_2;
else if (Condition_3)
Statement_3;
...
else
Statement_n;
示例
此例检查输入字符是否是小写字符、大写字符或数字。否则,输入字符不是字母字符。程序利用 else-if 阶梯。
下面再做一个类:
public class IfTest2
{
public System.Windows.Forms.TextBox textB;
public void Mychar(string c)
{
char s;
s=c[0];
if (Char.IsUpper(s))
textB.Text="这是大写字母";
else if (Char.IsLower(s))
textB.Text="这是小写字母";
else if (Char.IsDigit(s))
textB.Text="这是数字";
else
textB.Text="这不是字母或者数字";
}
}
只要把类的声明语句稍稍改一下,就可以使用这个类的方法:
private void button4_Click(object sender, System.EventArgs e)
{
IfTest2 Myclass;
Myclass=new IfTest2();
string s;
s=textBox1.Text;
Myclass.textB=textBox2;
Myclass.Mychar(s);
}
4-2 switch-case语句
switch 语句是一个控制语句,它通过将控制传递给其体内的一个 case 语句来处理多个选择。switch 语句的形式为:
switch (expression)
{
case constant-expression:
statement
jump-statement
[default:
statement
jump-statement]
}
此处:
expression
一个整型或字符串型表达式。
statement
控制传递给 case 或 default 时执行的嵌入语句。
jump-statement
将控制传递到 case 体外部的跳转语句。
constant-expression
根据此表达式的值将控制传递给特定的 case。
备注
控制传递给 constant-expression 与 expression 匹配的 case 语句。switch 语句可以包含任意数量的 case 实例,但同一 switch 语句中的两个case 常数不能具有相同的值。语句体从选定的语句开始执行,一直执行到 jump-statement 将控制传递到 case 体之外为止。
请注意,每个块(包括最后一个块,不管它是 case 语句还是 default 语句)后都要有 jump-statement。与 C++ switch 语句不同,C# 不支持从一个 case 标签显式贯穿到另一个 case 标签。如果要使 C# 支持从一个 case 标签显式贯穿到另一个 case 标签,可以使用 goto 一个 switch-case 或 goto default。
如果 expression 不匹配任何 constant-expression,则控制传递给可选的 default 标签后面的 statement。如果没有 default 标签,则控制传递到 switch 之外。
类:
public class CaseTest
{
public System.Windows.Forms.TextBox textB;
public void MyCase(string s)
{
textB.Clear();
int n = int.Parse(s);
int cost = 0;
switch(n)
{
case 1:
cost += 25;
//break 语句终止它所在的最近的封闭循环或条件语句。
//控制传递给终止语句后面的语句(如果有的话)
break;
case 2:
cost += 25;
goto case 1;
case 3:
cost += 50;
goto case 1;
default:
textB.Text="错误的选择,请输入 1, 2, or 3.";
break;
}
if (cost != 0)
textB.AppendText(cost.ToString());
}
}
事件程序:
private void button1_Click(object sender, System.EventArgs e)
{
CaseTest Myclass;
Myclass=new CaseTest();
string s;
s=textBox1.Text;
Myclass.textB=textBox2;
Myclass.MyCase(s);
}
4-3 for循环
for 循环重复执行一个语句或一个语句块,直到指定的表达式求得 false 值为止。它的形式为:
for ([initializers]; [expression]; [iterators]) statement
此处:
initializers
初始化循环计数器的表达式或赋值语句的逗号分隔列表。
expression
一个表达式,可隐式转换为 bool 或包含重载 true 和 false 运算符的类型。此表达式用于测试循环终止条件。
iterators
递增或递减循环计数器的表达式语句。
statement
要执行的嵌入语句。
备注
for 语句按下列方式重复执行 statement:
首先,计算 initializers。
然后,当 expression 求得 true 值时,执行 statement 并计算 iterators。 当 expression 求得 false 值时,控制传递到循环之外。
由于 expression 的测试发生在循环的执行之前,因此 for 语句执行零次或更多次。
for 语句的所有表达式都是可选的;例如,下列语句用于写一个无限循环:
for (;;) {
...
}
示例
private void button2_Click(object sender, System.EventArgs e)
{
textBox2.Text="";
for (int i = 1; i <= 5; i++)
textBox2.AppendText(i.ToString()+"\r\n");
//"\r\n"为转意字符
//\r回车
//\n换行
//\b退格
//\f换页
//\'单引号
//\"双引号
}
4-4 foreach, in语句
foreach 语句为数组或对象集合中的每个元素重复一个嵌入语句组。foreach 语句用于循环访问集合以获取所需信息,但不应用于更改集合内容以避免产生不可预知的副作用。此语句的形式如下:
foreach (type identifier in expression) statement
此处:
type
identifier 的类型。
identifier
表示集合元素的迭代变量。
expression
对象集合或数组表达式。集合元素的类型必须可以转换为 identifier 类型。
statement
要执行的嵌入语句。
备注
嵌入语句为数组或集合中的每个元素继续执行。当为集合中的所有元素,完成迭代后,控制传递给 foreach 块之后的下一个语句。
示例
此例搜索一个整数数组中的偶数和奇数。每类数字的计数器存储该类数字出现的次数。
private void button3_Click(object sender, System.EventArgs e)
{
int odd = 0, even = 0;
int[] arr = new int [] {0,1,2,5,7,8,11};
foreach (int i in arr)
{
///模数运算符 (%) 计算第二个操作数除第一个操作数后的余数。
if (i%2 == 0)
even++;
else
odd++;
}
textBox2.Text="";
for (int i = 0; i <= 6; i++)
textBox2.AppendText(arr[i].ToString()+" ");
textBox2.AppendText("\r\n");
textBox2.AppendText("偶数:"+even.ToString()+"\r\n");
textBox2.AppendText("奇数:"+odd.ToString()+"\r\n");
}
4-5 while循环
while 语句执行一个语句或一个语句块,直到指定的表达式求得 false 值为止。它的形式为:
while (expression) statement
此处:
expression
一个表达式,可隐式转换为 bool 或包含重载 true 和 false 运算符的类型。此表达式用于测试循环终止条件。
statement
要执行的嵌入语句。
备注
由于 expression 的测试发生在循环的执行之前,因此 while 循环执行零次或更多次。
当 break、goto、return 或 throw 语句将控制传递到循环之外时可以终止 while 循环。若要将控制传递给下一个迭代但不退出循环,请使用 continue 语句。
示例:
private void button4_Click(object sender, System.EventArgs e)
{
int n = 1;
textBox2.Text="";
while (n < 6)
{
textBox2.AppendText("循环次数 "+n.ToString()+"\r\n");
n++;
}
}
4-6 do循环
do 语句重复执行一个语句或一个语句块,直到指定的表达式求得 false 值为止。它的形式为:
do statement while (expression);
此处:
expression
一个表达式,可隐式转换为 bool 或包含重载 true 和 false 运算符的类型。此表达式用于测试循环终止条件。
statement
要执行的嵌入语句。
备注
与 while 语句不同,do 语句的体循环至少执行一次,与 expression 的值无关。
示例,注意在第二个例子中,虽然条件求得 false 值,但循环将执行一次。
private void button5_Click(object sender, System.EventArgs e)
{
int x;
int y = 0;
textBox2.Text="";
do
{
x = y++;
textBox2.AppendText("循环次数 "+x.ToString()+"\r\n");
}
while(y < 5);
textBox2.AppendText("第二个例子: "+"\r\n");
int n = 10;
do
{
textBox2.AppendText("n的数字 "+n.ToString()+"\r\n");
n++;
} while (n < 6);
}
4-7 调转语句
break 语句终止它所在的最近的封闭循环或条件语句。控制传递给终止语句后面的语句(如果有的话)。break 语句的形式为:
break;
示例
在此例中,条件语句包含一个可以从 1 计数到 100 的计数器;但 break 语句在计数达到 4 后终止循环。
// statements_break.cs
using System;
class BreakTest
{
public static void Main()
{
for (int i = 1; i <= 100; i++)
{
if (i == 5)
break;
Console.WriteLine(i);
}
}
}
输出
1
2
3
4
示例
此例在 switch 语句中演示了 break 的用法。
// statements_break2.cs
// break and switch
using System;
class Switch
{
public static void Main()
{
Console.Write("Enter your selection (1, 2, or 3): ");
string s = Console.ReadLine();
int n = Int32.Parse(s);
switch(n)
{
case 1:
Console.WriteLine("Current value is {0}", 1);
break;
case 2:
Console.WriteLine("Current value is {0}", 2);
break;
case 3:
Console.WriteLine("Current value is {0}", 3);
break;
default:
Console.WriteLine("Sorry, invalid selection.");
break;
}
}
}
输入
1
示例输出
Enter your selection (1, 2, or 3): 1
Current value is 1
如果输入了 4,则输出为:
Enter your selection (1, 2, or 3): 4
Sorry, invalid selection.
注意一下,C#里面break的用法和java并不一样。
第五节 方法调用语句
1) ref参数声明方式
方法参数上的 ref 方法参数关键字使方法引用传递到方法的同一个变量。当控制传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。
若要使用 ref 参数,必须将参数作为 ref 参数显式传递到方法。ref 参数的值被传递到 ref 参数。
传递到 ref 参数的参数必须最先初始化。将此方法与 out 参数相比,后者的参数在传递到 out 参数之前不必显式初始化。
如果两个方法的声明只在 ref 的使用方面不同,则会发生重载。
属性不是变量,不能作为 ref 参数传递。
实例:
public static void TestRef(ref char i)
{
i = 'b';
}
public static void TestNoRef(char i)
{
i = 'c';
}
private void button1_Click(object sender, System.EventArgs e)
{
char i = 'a';
TestRef(ref i);
textBox1.Text=i.ToString();
TestNoRef(i);
textBox2.Text=i.ToString();
}
}
2)out参数声明方式
方法参数上的 out 方法参数关键字使方法引用传递到方法的同一个变量。当控制传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。
当希望方法返回多个值时,声明 out 方法非常有用。使用 out 参数的方法仍然可以返回一个值。一个方法可以有一个以上的 out 参数。
若要使用 out 参数,必须将参数作为 out 参数显式传递到方法。out 参数的值不会传递到 out 参数。
不必初始化作为 out 参数传递的变量。然而,必须在方法返回之前为 out 参数赋值。
如果两个方法的声明仅在 out 的使用方面不同,则会发生重载。
属性不是变量,不能作为 out 参数传递。
有关传递数组的信息,请参阅使用 ref 和 out 传递数组。
示例
public static int TestOut(out char i)
{
i = 'b';
return -1;
}
private void button2_Click(object sender, System.EventArgs e)
{
char i;
textBox1.Text=TestOut(out i).ToString();
textBox2.Text=i.ToString();
}
3)params参数声明方式:
params 关键字可以指定在参数数目可变处采用参数的方法参数。
在方法声明中的 params 关键字之后不允许任何其他参数,并且在方法声明中只允许一个 params 关键字。
示例
void Useparams(params int[] list)
{
listBox1.Items.Clear();
for (int i=0;i<list.Length;i++)
{
listBox1.Items.Add(list[i].ToString());
}
}
void Useparams2(params object[] list)
{
listBox1.Items.Clear();
for (int i=0;i<list.Length;i++)
{
listBox1.Items.Add((object)list[i].ToString());
}
}
//传递一个参数
private void button1_Click(object sender, System.EventArgs e)
{
Useparams(1);
}
//传递三个参数
private void button2_Click(object sender, System.EventArgs e)
{
Useparams(1,2,3);
}
//传递不同类型的参数
private void button3_Click(object sender, System.EventArgs e)
{
Useparams2(1,'a',"test");
}
//传递数组
private void button4_Click(object sender, System.EventArgs e)
{
int[] myarray=new int[3]{10,20,30};
Useparams(myarray);
}
4)new 关键字
在 C# 中,new 关键字可用作运算符或修饰符。
new 运算符 用于在堆上创建对象和调用构造函数。
new 修饰符 用于隐藏基类成员的继承成员。
a>new 运算符
new 运算符用于创建对象和调用构造函数,例如:
Class1 MyClass = new Class1();
也用于为值类型调用默认的构造函数,例如:
int myInt = new int();
在上一个语句中,myInt 初始化为 0,它是 int 类型的默认值。该语句的效果等同于:
int myInt = 0;
有关默认值的完整列表,请参阅默认值表。
记住,为结构声明默认的构造函数是一个错误,因为每一种值类型都隐式具有一个公共的默认构造函数。但是,可以在结构类型上声明参数化的构造函数。
值类型对象(例如结构)是在堆栈上创建的,而引用类型对象(例如类)是在堆上创建的。
不能重载 new 运算符。
第六节 数组
7-1 一维数组
可以如下例所示声明一个由 5 个整数组成的数组:
int[] myArray = new int [5];
此数组包含从 myArray[0] 到 myArray[4] 的元素。new 运算符用于创建数组并将数组元素初始化为它们的默认值。在此例中,所有数组元素都初始化为零。
可以用相同的方式声明存储字符串元素的数组。例如:
string[] myStringArray = new string[6];
数组初始化
可以在声明数组时将其初始化,在这种情况下不需要级别说明符,因为级别说明符已经由初始化列表中的元素数提供。例如:
int[] myArray = new int[] {1, 3, 5, 7, 9};
可以用相同的方式初始化字符串数组。下面声明一个字符串数组,其中每个数组元素用每天的名称初始化:
string[] weekDays = new string[]
{"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};
如果在声明数组时将其初始化,则可以使用下列快捷方式:
int[] myArray = {1, 3, 5, 7, 9};
string[] weekDays = {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};
可以声明一个数组变量但不将其初始化,但在将数组分配给此变量时必须使用 new 运算符。例如:
int[] myArray;
myArray = new int[] {1, 3, 5, 7, 9}; // OK
myArray = {1, 3, 5, 7, 9}; // Error
值类型数组和引用类型数组
请看下列数组声明:
MyType[] myArray = new MyType[10];
该语句的结果取决于 MyType 是值类型还是引用类型。如果是值类型,则该语句将创建一个由 10 个 MyType 类型的实例组成的数组。如果 MyType 是引用类型,则该语句将创建一个由 10 个元素组成的数组,其中每个元素都初始化为空引用。
将数组作为参数传递
可以将初始化的数组传递给方法。例如:
PrintArray(myArray);
也可以在一个步骤中初始化并传递新数组。例如:
PrintArray(new int[] {1, 3, 5, 7, 9});
示例
在下例中,初始化一个字符串数组并将其作为参数传递给 PrintArray 方法(该数组的元素显示在此方法中):
// cs_sd_arrays.cs
using System;
public class ArrayClass
{
static void PrintArray(string[] w)
{
for (int i = 0 ; i < w.Length ; i++)
Console.Write(w[i] + "{0}", i < w.Length - 1 ? " " : "");
Console.WriteLine();
}
public static void Main()
{
// Declare and initialize an array:
string[] WeekDays = new string []
{"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};
// Pass the array as a parameter:
PrintArray(WeekDays);
}
}
输出
Sun Sat Mon Tue Wed Thu Fri
7-2 多维数组
数组可以具有多个维度。例如,下列声明创建一个四行两列的二维数组:
int[,] myArray = new int[4,2];
另外,下列声明创建一个三维(4、2 和 3)数组:
int[,,] myArray = new int [4,2,3];
数组初始化
可以在声明数组时将其初始化,如下例所示:
int[,] myArray = new int[,] {{1,2}, {3,4}, {5,6}, {7,8}};
也可以初始化数组但不指定级别:
int[,] myArray = {{1,2}, {3,4}, {5,6}, {7,8}};
如果要声明一个数组变量但不将其初始化,必须使用 new 运算符将数组分配给此变量。例如:
int[,] myArray;
myArray = new int[,] {{1,2}, {3,4}, {5,6}, {7,8}}; // OK
myArray = {{1,2}, {3,4}, {5,6}, {7,8}}; // Error
也可以给数组元素赋值,例如:
myArray[2,1] = 25;
将数组作为参数传递
可以将初始化的数组传递给方法。例如:
PrintArray(myArray);
也可以在一个步骤中初始化并传递新数组。例如:
PrintArray(new int[,] {{1,2}, {3,4}, {5,6}, {7,8}});
示例
在此例中,初始化一个二维数组并将其传递给 PrintArray 方法(该数组的元素显示在此方法中)。
// cs_td_arrays.cs
using System;
public class ArrayClass
{
static void PrintArray(int[,] w)
{
// Display the array elements:
for (int i=0; i < 4; i++)
for (int j=0; j < 2; j++)
Console.WriteLine("Element({0},{1})={2}", i, j, w[i,j]);
}
public static void Main()
{
// Pass the array as a parameter:
PrintArray(new int[,] {{1,2}, {3,4}, {5,6}, {7,8}});
}
}
输出
Element(0,0)=1
Element(0,1)=2
Element(1,0)=3
Element(1,1)=4
Element(2,0)=5
Element(2,1)=6
Element(3,0)=7
Element(3,1)=8
7-3 交错数组
交错数组是元素为数组的数组。交错数组元素的维度和大小可以不同。交错数组有时称为“数组的数组”。本主题包含声明、初始化和访问交错数组的示例。
下面声明一个由三个元素组成的一维数组,其中每个元素都是一个一维整数数组:
int[][] myJaggedArray = new int[3][];
必须初始化 myJaggedArray 的元素后才可以使用它。可以如下例所示初始化元素:
myJaggedArray[0] = new int[5];
myJaggedArray[1] = new int[4];
myJaggedArray[2] = new int[2];
每个元素都是一个一维整数数组。第一个元素是由 5 个整数组成的数组,第二个是由 4 个整数组成的数组,而第三个是由 2 个整数组成的数组。
也可以使用初始值设定项用值填充数组元素,在这种情况下不需要数组大小,例如:
myJaggedArray[0] = new int[] {1,3,5,7,9};
myJaggedArray[1] = new int[] {0,2,4,6};
myJaggedArray[2] = new int[] {11,22};
还可以在声明数组时将其初始化,如:
int[][] myJaggedArray = new int [][]
{
new int[] {1,3,5,7,9},
new int[] {0,2,4,6},
new int[] {11,22}
};
可以使用下列快捷方式(注意在元素初始化中不能省略 new 运算符,因为元素没有默认初始化):
int[][] myJaggedArray = {
new int[] {1,3,5,7,9},
new int[] {0,2,4,6},
new int[] {11,22}
};
可以如下例所示访问个别数组元素:
// Assign 33 to the second element of the first array:
myJaggedArray[0][1] = 33;
// Assign 44 to the second element of the third array:
myJaggedArray[2][1] = 44;
可以混合使用交错数组和多维数组。下面声明和初始化一个一维交错数组,该数组包含大小不同的二维数组元素:
int[][,] myJaggedArray = new int [3][,]
{
new int[,] { {1,3}, {5,7} },
new int[,] { {0,2}, {4,6}, {8,10} },
new int[,] { {11,22}, {99,88}, {0,9} }
};
可以如下例所示访问个别元素,该示例显示第一个数组的元素 [1,0] 的值(值为 5):
Console.Write("{0}", myJaggedArray[0][1,0]);
示例
下例生成数组 myArray,而此数组的元素为数组。每一个数组元素都有不同的大小。
// cs_array_of_arrays.cs
using System;
public class ArrayTest
{
public static void Main()
{
// Declare the array of two elements:
int[][] myArray = new int[2][];
// Initialize the elements:
myArray[0] = new int[5] {1,3,5,7,9};
myArray[1] = new int[4] {2,4,6,8};
// Display the array elements:
for (int i=0; i < myArray.Length; i++)
{
Console.Write("Element({0}): ", i);
for (int j = 0 ; j < myArray[i].Length ; j++)
Console.Write("{0}{1}", myArray[i][j],
j == (myArray[i].Length-1) ? "" : " ");
Console.WriteLine();
}
}
}
输出
Element(0): 1 3 5 7 9
Element(1): 2 4 6 8
7-4 使用 ref 和 out 传递数组
与所有的 out 参数一样,在使用数组类型的 out 参数前必须先为其赋值,即必须由接受方为其赋值。例如:
public static void MyMethod(out int[] arr)
{
arr = new int[10]; // definite assignment of arr
}
与所有的 ref 参数一样,数组类型的 ref 参数必须由调用方明确赋值。因此不需要由接受方明确赋值。可以将数组类型的 ref 参数更改为调用的结果。例如,可以为数组赋以 null 值,或将其初始化为另一个数组。例如:
public static void MyMethod(ref int[] arr)
{
arr = new int[10]; // arr initialized to a different array
}
下面的两个示例说明 out 和 ref 在将数组传递给方法上的用法差异。
示例 1
在此例中,在调用方(Main 方法)中声明数组 myArray,并在 FillArray1 方法中初始化此数组。然后将数组元素返回调用方并显示。
public void FillArray1(out int[] myArray)
{
// 初始化数组:
myArray = new int[5] {1, 2, 3, 4, 5};
}
//out
private void button1_Click(object sender, System.EventArgs e)
{
int[] myArray; //定义数组
//调用FillArray1并out数组,事先没有初始化
FillArray1(out myArray);
//显示
listBox1.Items.Clear();
for (int i=0; i < myArray.Length; i++)
listBox1.Items.Add(myArray[i].ToString());
}
输出
1
2
3
4
5
示例 2
在此例中,在调用方中初始化数组 myArray,并通过使用 ref 参数将其传递给 FillArray2 方法。在 FillArray2 方法中更新某些数组元素。然后将数组元素返回调用方并显示。
public static void FillArray2(ref int[] arr)
{
// 如果传递进来的数组初始化为null,则建立一个10个单元空数组:
if (arr == null)
arr = new int[10];
//填入数组数据:
arr[0] = 123;
arr[4] = 1024;
}
//ref
private void button2_Click(object sender, System.EventArgs e)
{
//初始化:
int[] myArray = {1,2,3,4,5};
//int[] myArray = null;
// ref调用:
FillArray2(ref myArray);
// 显示数组数据:
listBox1.Items.Clear();
for (int i = 0; i < myArray.Length; i++)
listBox1.Items.Add(myArray[i].ToString());
}
输出
123
2
3
4
1024
如果初始化改为:
int[] myArray = null;
则结果为
想想为什么呢?
7-5 数组的排序和查找
微软公司经过多年摸索,在.NET平台上大大改进了数组的性能,现在,数组可以排序、搜索元素等,过去编程人员需要花费大量时间进行数组运算的编程,现在可能轻而易举就解决了。
除数组以外,还有大量的集合类型,放在名称空间的System.Collections中,其中提供的一些类诸如ArrayList、Hashtable都有十分良好的性能。
下面分别进行讨论.
a>数组排序
数组排序主要是针对一维数组,下面是实现排序的实例:
int[] MyArray1= {3, 6, 1, 0, 5, 7, 8, 2, 9, 4};
listBox1.Items.Clear();
System.Array.Sort(MyArray1);
for (int i=0;i<10;i++)
{
listBox1.Items.Add(MyArray1[i]);
}
有时候可以利用另一个数组的值来排序第一个数组,下面的例子是利用
MyArray2的值来排序MyArray1。
int[] MyArray1= {3, 6, 1, 0, 5, 7, 8, 2, 9, 4};
int[] MyArray2= {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
listBox1.Items.Clear();
System.Array.Sort(MyArray2,MyArray1);
for (int i=0;i<10;i++)
{
listBox1.Items.Add(MyArray1[i]);
这个方法的一个实际的例子,是使一个数组按照字符的多少来排序,请看下面的例子:
string[] MyArray1= {"aa", "cccccc", "vvvv", "s"};
int[] MyArray2=new int[4];
listBox1.Items.Clear();
for (int i=0;i<4;i++)
{
MyArray2[i] = MyArray1[i].Length;
}
System.Array.Sort(MyArray2, MyArray1);
for (int i=0;i<4;i++)
{
listBox1.Items.Add(MyArray1[i]);
}
b>数组查找
下面的例子可以理解在树组中如何查找匹配的内容,给出的是数组的索引值。
使用IndexOf查找不需要数组排序:
string[] MyArray1= {"aa", "cccccc", "vvvv", "s"};
int[] MyArray2= {3, 6, 1, 0, 5, 7, 8, 2, 9, 4};
listBox1.Items.Clear();
listBox1.Items.Add(System.Array.IndexOf(MyArray1, "vvvv"));
listBox1.Items.Add(System.Array.IndexOf(MyArray2, 5));
使用BinarySearch可以在已经排序的数组中快速查找。
System.Array.Sort(MyArray1);
for(int i=0;i<4;i++)
{
listBox1.Items.Add(MyArray1[i]);
}
listBox1.Items.Add(System.Array.BinarySearch(MyArray1, "s"));
结果:

|