C#简介-什么是C#
C#是可用于创建运行在.NET CLR上的应用程序语言之一,它从C和C++语言演化而来,是microsoft 专门为使用.NET平台而创建的。因为C#是近期发展起来的,所以吸取了以前的教训,考虑了其他语言的许多有点,并解决了它们的问题。
使用C#开发应用程序比使用C++简单,因为其语法比较简单。但是,C#是一种强大的语言,在C++中能完成的任何利用C#也能完成。
C#代码通常比C++略长一些。这是因为C#是一种类型安全的语言(与C++不同)。在外行人看来,这表示一旦为某些数据指定了类型,就不能转换为另一个不相关的类型。所以,在类型之间转换时,必须遵守严格的规则。执行相同的任务时,用C#编写的代码也比C++长。但C#代码更加健壮,调试也比较简单。
C#简介-什么是.NET F ramework
.NET Framework是microsoft为开发应用程序而创建的一个富有革命性的新平台。
尽管.NET Framework现在的版本要运行在woindows操作系统上,但一位将推出运行在其他操作系统上的版本,例如mono,包括各种linux版本合mac OS。另外还可以在个人数字助手(PDA)类设备合一些智能电话上使用。
.NET Framework可以创建windows应用程序、web应用程序、web服务合其他各种类型的应用程序。
.NET Framework的设计方式保证它可以使用各种语言,C++/vb/j#/甚至一些旧的语言等。当然重点还是我们的C#,毕竟它是microsoft专门为.NET Framework设计的程序设计语言。
为了执行C#代码,必须把它们转换为操作系统能够理解的语言,即本机代码,这种转换的过程称为编译代码,由编译器执行。在.NET Framework下分为两个阶段。
首先,在编译使用.NET Framework库的代码时,不是立即创建操作系统特定的本机代码,而是把代码编译为Microsoft中间语言(Microsoft Intermediate Language,MSIL)代码,这些代码是可以跨平台的。
显然,要执行应用程序这些是不够的,还需要进行二次编译。就是刚上述说的MSIL中间代码编译为用于OS和目标及其结构的本机代码。这样OS才能执行应用程序。
程序集
在编译应用程序时,所创建的MSIL代码被存储在一个程序集中,程序集包括可执行的应用程序文件(这些文件可以在windows上运行,不需要其他程序,扩展名是.exe)和其他应用程序使用的库(其扩展名是.dll)。
除了包含MSIL外,程序集还包含元信息(即程序集中包含的数据的信息,也称为元数据)和可选的资源(MSIL使用的其他数据,例如声音文件和图片)。元信息可以把需要的数据添加到系统注册表中,而这种情形在使用其他平台进行开发时常常不能够实现。
托管代码
在把代码编译为MSIL,再用编译器把它编译为本机代码后,CLR(公共语言运行库)的任务还没有完全完成。用.NET Framework编写的代码在运行(runtime)是托管的。即CLR会管理这应用程序的内存、处理安全性、允许进行跨语言调试等。
垃圾回收
托管代码最重要的一个功能是垃圾回收(garbage collection)。这可以确保应用程序不再使用某些内存时,这些内存就会被完全释放。不像与以前,这项工作主要由程序员负责,代码中的几个简单错误就会把大块内存分配到错误的地方,使这些内存神秘失踪。这通常让计算机的速度逐渐变慢,甚至导致系统崩溃。
这里需要注意的事,因为这项工作在不可预知的时间进行,所以在设计应用程序时,必须记得要进行这样的检查。毕竟它没有很人性化,需要许多内存才能运行的代码应自己执行这样的检查,而不是坐等垃圾回收,呵呵。
把它们组合在一起
在继续学习之前,先总结一下上述创建.NET应用程序所需要的步骤:
1、使用某种.NET兼容语言(如C#)编写好应用程序代码。
2、把代码编译为MSIL(中间语言),并存储在程序集中。
3、执行代码,将MSIL编译为本机代码。
4、在托管的CLR环境下运行本机代码,执行应用程序。
编写C#程序-控制台应用程序
试试看,创建一个简单的控制台应用程序:
1、选择File|New|Project...菜单项,创建一个新的控制台应用程序项目。
2、在所显示窗口的Project Types:窗格中选择Visual C#节点,在Temlpates:窗格中选择Console Application项目类型。当然你也可以自定义保存项目的路径。
3、单击OK按钮完成创建。
4、初始化项目后,在主窗口显示的文件里添加如下代码行:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
//out text to the screen.
Console.WriteLine("hello!");
Console.ReadKey();
}
}
}
5、选择Debug|Start菜单项。稍后就会在cmd窗口中显示“hello!”字样的结果。
6、按下任意键,退出应用程序。
现在不仔细研究这个项目使用的代码,而关心如果使用VS启动和运行代码。可以看出,VS做了许多工作,大大简化了编译和执行代码的过程。执行这些简单的步骤还有多种方式。例如在创建一个新项目时也可以按下纯ctrl+shift+n,还可以单击工具栏上相应的图标。
编写C#程序-Windows Application应用程序
通常把代码当做Windos应用程序的一部分来说明,要比通过控制台窗口或命令行提示简单一些。下面用户界面构成块来组合一个用户界面。
现在介绍建立用户界面的基础知识,说明如何启动和运行windows应用程序,但并不详细讨论应用程序实际完成的工作。
试试看,创建一个简单的Windows应用程序:
1、在以前的位置创建一个类型为Windows Application的新项目,其默认名称是WindowsApplication1。
2、单击OK,创建项目后,应该会看到一个空白的windows窗体。把鼠标指针移到屏幕左边的Toolbox 栏上,然后找到windows forms选项卡上的button选项双击,就会在应用程序的主窗体(form1)上添加一个按钮。
3、双击刚才添加到窗体中的按钮。
4、现在应显示Form1.cs中的C#代码。进行如下修改:
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("hello!");
}
5、运行应用程序。
6、单击显示出来的按钮,打开一个消息对话框,显示信息“hello!”。
7、像每个标准的windows应用程序那样,单击右上角的x,退出应用程序。
VS又一次完成了许多工作,刚才创建的应用程序与其他窗口的行为方式相同——可以移动、重新设置其大小、最小化等。我们不必编写任何代码,它就可以工作。我们添加的按钮也是这样。双击按钮,VS 就知道我们像添加一些代码,当程序运行时,用户单击按钮,就执行我们编好的代码。只要提供了这段代码,就可以得到按钮单击的所有功能。
变量和表达式-C#基本语法
计算机程序最基本的描述也许是一系列处理数据的操作,即使是最复杂的实例,这个论述也是正确的。应用程序的用户虽然看不到它们,但这些操作总是在后台上进行。我们在屏幕上进行的任何操作,无论是移动鼠标指针,单击图标,或在子处理器上输入文本,都会改变内存中的数据。
如果说计算机程序是在对数据执行操作,这说明我们需要某种存数数据的方式,以及处理它们的一些方法。这两种功能是由变量和表达式提供的。在开始之前,应先了解一下C#编程的基本语法,因为我们需要一个环境来学习使用C#语言中的变量和表达式。
C#代码的外观和操作方式与C++和Java非常类似。初看起来,其语法可能比较混乱,不像书面英语和其他语言。但是,在C#编程中,使用的样式是比较清晰的,不用花太多力气就可以编写出刻度性很强的代码。
C#编译器不考虑空格、回车符或tab字符等空白字符。这样格式化代码时又很大的自由度,但遵循某些规则将有助于提高代码的可读性。
C#是一个块结构语言,所有的语句都是代码块的一部分。这些块用花括号来界定,代码块可以包含任意多行语句,或者根本不包含语句。
简单的C#代码块如下所示:
{
;
;
}
其中部分并不是真正的C#代码,而是用这个文本作为C#语句的占位符。注意这段代码中,第2、3行代码是同一个语句的第一部分。
这个简单的代码块中,还使用了缩进格式,使C#代码的可读性更高。这是一个标准规则,VS也会自动缩进代码。通常每个代码块都有自己的缩进级别。
{
;
{
;
;
}
;
}
前面代码的续行通常也要缩进的多一些,如上面第一个实例中的第3行代码。
这种样式并不是强制的,但如果不使用它,将大大降低可读性。
C#代码中,另一个常见的语句是注释。注释并不是严格意义上的C#代码。注释就是解释,即给代码添加描述性文本,编译器会忽略这些内容。
C#注释方法有三种,可以在注释的开头加上“/*”,在末尾加上“*/”。这些注释符号可以在单独一行上,也可以在不同的行赏。
/* This is a comment*/
/* And so...
... is this!*/
但下面的语句是错误的:
/* Commnets often end with "*/" characters */
注释结束符号后的内容会被视为C#代码,因此产生错误。
另一个添加注释的方法使用“//”开始一个注释,但它的注释范围是一行。下面的语句是正确的:
//This is a different sort of commnet
但下面的语句会失败,因为第二行代码会解释为C#代码:
// So is this,
but this bit isn't.
还有第三类注释,严格的说,这是//语法的扩展。它们都是单行注释,用三个“/”符号来开头,而不是两个。
/// A special comment
正常情况下,编译器会户忽略它们,但可以配置VS在编译项目时,提取这些注释后面的文本,创建一个特殊格式的文本文件,该文件可用于创建文档说明书。
另外,C#代码是区分大小写的。与其他语言不同,必须使用正确的大小写形式输入代码。看看下面的这行代码,它将在后面的一个示例中使用:
Console.WriteLine("The first app in Beginning C# Programming!");
C#编译器能够理解这行代码,因为Console.WriteLine()命令的大小写形式是正确的。一旦出现大小写拼写错误,语句都不能工作!
变量和表达式-C#控制台应用程序的基本结构
下面来看一个控制台应用程序示例(ConsoleApplication1),研究一下它的结构。其代码如下所示:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
//out text to the screen.
Console.WriteLine("hello!");
Console.ReadKey();
}
}
}
可以立即看出,上一节讨论的所有语法元素这里都有。其中有分好、花括号、注释和适当的缩进。
看一下代码中最重要的部分:
static void Main(string[] args)
10 {
11//out text to the screen.
12 Console.WriteLine("hello!");
13 Console.ReadKey();
14 }
在运行控制台应用程序时,就运行这段代码,更准确的说,是运行花括号中的代码块。注释行不做任何事情。其他两行代码在控制台窗口中输出了一些文本,并等待一个响应。目前我们不必关心它的具体机制。
变量和表达式-变量
变量关系到数据的存储。实际上,可以把计算机内存中的变量看作架子上的盒子。在这些盒子中,可以放入一些东西,当然也可以再把它们取出来,或者只是看看盒子里是否有东西。变量也是这样,数据可放在变量中,可以从变量中取出数据或查看它们。
尽管计算机中的所有数据都是一样的东西(一组0和1),但变量有不同的含义,成为类型。就比如虽然都是盒子,但盒子有不同的形状和尺寸。某些东西只适合放特定的盒子中。建立这个类型系统的原因是,不同类型的数据需要用不同的方法来处理。变量限定为不同的类型,可以避免混淆它们。例如,组成数字图片的0和1序列与组成声音文件的0和1序列的处理方式是不同的。
C#中,要使用变量,需要声明它们。即给变量指定名称和类型。声明之后就可以放数据了。如下所示:
注意,未声明的变量,代码不会被编译。
那么我们该如何使用类型呢?实际上,可以使用的变量类型是无限多的。其原因是可以自定义类型存储各种复杂的数据。但总有一些数据类型是必要的,例如存储数值的变量。因此我们应了解一些简单的默认的数据类型。
变量和表达式-简单的数据类型
简单的数据类型就是组成应用程序中最基本的部件类型。例如数值和布尔值(true或false)。几个简单的数据类型还可以组成比较复杂的类型。大多数简单类型都是存储数值的。而且不止一种。
数值类型过多的原因是在计算机内存中,把数字作为一系列的0和1来存储的机制。对于整数值,用一定的位。如果超过这个位就不能存储在这个变量中。
例如,有一个变量存储了2位,在证书和表示该证书的位之间的映射如下所示:
0=00
1=01
2=10
3=11
要存储更大的数,就要有更多的位(例如,3位可以存储0-7的数)。
为了更加规范的整合存储的位所占用的内存,许多不同的整数类型可以用于存储不同范围的数值,从而占用不同的内存空间(最多64位),如下所示:
sbyte:在-128~127之间的整数
byte:在0~255之间的整数
short:在-32768~32767之间的整数
ushort:在0~65535之间的整数
int:在-2147483648~2147483647之间的整数
uint:在0~4294967295之间的整数
long:在-9223372036854775808~9223372036854775807之间的整数
ulong:在0~18446744073709551615之间的整数
注意,“u”是unsigned的缩写,表示不能在这些类型的变量中存储负号。
当然,除了整数以外,还可以存储浮点数,它们不是整数。可以使用的浮点数类型有3种:float,double 和decimal。
float:近似最小值1.5*10(-45)/近似最大值3.4*10(38)
double:近似最小值5.0*10(-324)/近似最大值1.7*10(308)
decimal:近似最小值1.0*10(-28)/近似最大值7.9*10(28)
除了数值类型外,还有另外三种简单类型:
char:一个Unicode字符,存储0~65535之间的整数
bool:布尔值,true或false
string:一组字符
组成string的字符数没有上限,因为它可以使用可变大小的内存。
布尔类型bool是C#中最常用的一种变量类型,类似的类型在其他语言的代码中非常丰富。当编写应用程序的逻辑流程时,一个可以是true或false的变量可以产生分支作用。例如,考虑一下有多少问题可以用true或flash(可以理解成yes和no)来回答。执行变量值之间的比较或检查输入的有效性就是后面使用布尔变量的两个编程示例。
做一下下面这个例子,声明和使用它们。当然还包括赋值,再输出这些值。
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
int myInteger;
string myString;
myInteger = 17;
myString = "my String is:";
Console.WriteLine("{0}{1}.",myString,myInteger);
Console.ReadKey();
}
}
}
我们完成了3个任务:
1、声明两个变量
2、给这两个变量赋值
3、将这两个变量的值输出到控制台上
变量和表达式-变量的声明和赋值
声明一个变量,例如:
int age;
然后用=赋值运算符给变量赋值:
age = 21;
变量在使用前,必须初始化。上面的赋值语句可以当做初始化语句。
这里还可以做两件事,用户可以在C#代码中看到。第一是同时声明多个类型相同的变量,方法是:在类型的后面用逗号分隔变量名,如下所示:
int xSize ySize;
其中xSize和ySize都声明为整数类型。
第二个技巧是在声明变量的同时赋值,即把两行代码合并在一起:
int age = 21;
int xSize = 4,ySize = 5;
xSize和ySize被赋予不同的值。
注意下面的代码:
int xSize,ySize = 5;
其结果是ySize被初始化,而xSize仅进行了声明,而不是它们都等于5。
表达式-数学运算符
介绍完声明和初始化变量,下面该处理它们了。C#包含许多进行这类处理的运算符,包括前面使用过的=赋值运算符,把变量和字面值与运算符符合起来,就可以创建表达式,它是计算的基本建立块。
运算符的范围有简单的,也有非常复杂的。其中一些可能只在数学应用程序中使用。简单的操作包括所有的基本计算操作,而复杂的操作则包括通过变量里的内容的二进制表示来处理它们。还有专门用于处理布尔值的逻辑运算符。
运算符大致分为3类。
一元运算符,处理一个操作数
二元运算符,处理两个操作数
三元运算符,处理三个操作数
大多数运算符都是二元运算符,只有几个一元运算符和一个三元运算符,即条件运算符(条件运算符返回布尔值)。
数学运算符只包括一元运算符和二元运算符,共有5个,其中2个有二元和一元两种形式。
+(二元):表示两数相加。
-(二元):表示两数相减。
*(二元):表示两数相乘。
/(二元):表示两数相除。
%(二元):表示两数相除的余数(求模)
+(一元):表示变量1的值等于变量2的值
-(一元):表示变量1的值等于变量2的值除/乘以-1
+(一元)运算符有点奇怪,因为它对结果没有影响。它不会把值改成正的;如果变量2是-1,则+变量2仍是-1。但这是一个普遍认可的运算符,所以也把它包含进来。最常用的是你可以定制它的操作,在运算符的重载时会介绍它。
上面都是简单的数值类型,因为使用其他简单类型,结果可能不太清晰。如果把两个布尔值加在一起会得到什么结果?此时,如果对bool变量使用+或其他数学运算符,编译器就会报错。char变量的相加也
会让人搞不懂。记住,char变量实际上存储的就是数字,所以把两个char变量加在一起也会得到int类型的数字。这是一个隐式转换的例子,稍后会介绍这些,因为它也可以用到三个变量混合类型的情况。
二元运算符+在用于字符串类型变量时也是有意义的,表示两个字符串的连接值,但其他数学运算符不能用于字符串的处理。
这里介绍另外两个运算符是递增和递减运算符。它们都是一元运算符,可以以两种方式使用:放在操作数前面或后面。
++:var1的值是var2+1,var2递增1(var1=++var2)
--:var1的值是var2-1,var2递减1(var1=--var2)
++:var1的值是var2,var2递增1(var1=var2++)
--:var1的值是var2,var2递减1(var1=var2--)
这里的关键因素是这些运算符总是改变存储在操作数中的值。
*++总是使操作数加1
*--总是使操作数减1
var1中存储的结果有区别,其原因是运算符的位置决定了它什么时候发挥作用。吧运算符放在操作数前面,则操作数是在进行任何其他计算前受到运算符的影响,而把运算符放在操作数后面,则操作数是在完成表达式的计算后才起作用。
考虑下面的代码:
int var1,var2 = 5,var3 = 6;
var1 = var2++ * --var3;
答案是25。
下面做一个例子,说明如何使用数学运算符,并介绍另外两个有用的概念。代码提示用户输入一个字符串和两个数字,然后显示计算结果:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
double firstNumber, secondNumber;
string userName;
Console.WriteLine("请输入用户名:");
userName = Console.ReadLine();//将用户输入的用户名存储在变量userName中
Console.WriteLine("欢迎 {0}!",userName);
Console.WriteLine("请输入一个数字:");
firstNumber = Convert.ToDouble(Console.ReadLine());//将用户输入的第一个数字转换成double类型,并存储在firstNumber中
Console.WriteLine("请输入第二个数字");
secondNumber = Convert.ToDouble(Console.ReadLine());//将用户输入的第二个数字转换成double类型,并存储在secondNumber中
Console.WriteLine("{0}+{1}={2}",firstNumber,secondNumber,firstNumber + secondNumber);
Console.WriteLine("{0}-{1}={2}", firstNumber, secondNumber, firstNumber - secondNumber);
Console.WriteLine("{0}*{1}={2}", firstNumber, secondNumber, firstNumber * secondNumber);
Console.WriteLine("{0}/{1}={2}", firstNumber, secondNumber, firstNumber / secondNumber);
Console.WriteLine("{0}%{1}={2}", firstNumber, secondNumber, firstNumber % secondNumber);
Console.ReadKey();
}
}
}
表达式-赋值运算符
前面我们一直使用简单的=赋值运算符,其实还有其他赋值运算符也非常有用。
除了=运算符外,其他赋值运算符都以类似的方式工作。与=一样,它们都是根据运算符和右边的操作数,把一个值赋给左边的变量。
=:var1被赋予var2的值
+=:var1被赋予var1与var2的和
-=:var1被赋予var1与var2的差
*=:var1被赋予var1与var2的乘积
/=:var1被赋予var1与var2相除所得的结果
%=:var1被赋予var1与var2相除所得的余数
可以看出,这些运算符把var1也包括在计算过程中,下面的代码:
var1 += var2;
与下面的代码结果相同。
var1 = var1 + var2;
在继续学习之前,应该了解另一个比较重要的主题——命名空间。它们是.NET中提供应用程序代码容器的方式,这样就可以唯一的标识代码及其内容。命名空间也用作.NET Framework中给项分类的一种方式。大多数项都是类型定义,例如System.Int32等。
默认情况下,C#代码包含在全局命名空间中。这表示对于包含在这段代码中的项,只要按照名称进行引用,就可以由全局命名空间中的其他代码访问它们。可以使用namespace关键字位花括号中的代码块显示定义命名空间。如果在该命名空间代码的外部使用命名空间中的名称,就必须写出该命名空间中的限定名称。
限定名称包括它所有的继承信息。基本上,这意味着,如果一个命名空间中的代码要使用另一个命名空间中定义的名称,就必须包括对该命名空间的引用。限定名称在不同的命名空间级别之间使用据点字符(.)。
例如:
namespace LevelOne
{
//code in LevelOne namespace
//name "NameOne" defined
}
//code in global nam espace
这段代码定义了一个命名空间LevelOne,以及该命名空间中的一个名称NameOne。在命名空间LevelOne中编写的代码可以使用NameOne来引用该名称,不需要任何分类信息。但全局命名空间中的代码必须使用分类名称https://www.wendangku.net/doc/9f9450126.html,One来引用这个名称。
在命名空间中,使用关键字namespace还可以定义嵌套的命名空间。嵌套的命名空间通过其层次结构来引用,并使用据点区分层次结构的层次。这最好用一个例子来说明。考虑下面的命名空间:
namespace LevelOne
{
//code in LevelOne namespace
namespace LevelTwo
{
//code in LevelOne.LevelTwo nam espace
//name "NameTwo" defined
}
}
//code in global nam espace
在全局命名空间中NameTwo必须引用为https://www.wendangku.net/doc/9f9450126.html,Two,在LevelOne命名空间中,则可以引用位https://www.wendangku.net/doc/9f9450126.html,Two,在LevelOne.LevelTwo命名空间中,则可以引用为Nam eTwo。
要注意的是,名称由命名空间唯一定义的。可以在LevelOne和LevelTwo命名空间中定义名称NameThree:
namespace LevelOne
{
//name "NameThree" defined
namespace LevelTwo
{
//name "NameThree" defined
}
}
这里定义了两个互不干扰的NameThree,分别用LevelOne.Nam eThree和
https://www.wendangku.net/doc/9f9450126.html,Three访问。
创建了命名空间后,就可以使用using语句简化对它们包含的名称的访问。实际上,using语句的意思是“我们需要这个命名空间中的名称,所以不要每次总是要求对它们分类”。例如,在下面的代码中,LevelOne命名空间中的代码可以访问LevelOne.LevelTwo命名空间中的名称,而无需分类:
namespace LevelOne
{
using LevelTwo;
namespace LevelTwo
{
//name "NameTwo" defined
}
}
LevelOne命名空间中的代码现在可以直接使用NameTwo引用LevelTwo.Nam eTwo。
有时,与上面的NameThree示例一样,不同的命名空间中的相同名称会产生冲突,使系统崩溃(此时代码不能通过编译)。此时,可以为命名空间提供一个别名,作为using语句的一部分:
namespace LevelOne
{
using LT = LevelTwo;
//name "NameThree" defined
namespace LevelTwo
{
//name "NameThree" defined
}
}
LevelOne命名空间中的代码可以把https://www.wendangku.net/doc/9f9450126.html,Three引用位NameThree,把https://www.wendangku.net/doc/9f9450126.html,Three引用为https://www.wendangku.net/doc/9f9450126.html,Three。
using语句可以应用到包含它们的命名空间,以及该命名空间中包含的嵌套命名空间中。在上面的代码中,全局命名空间不能直接使用https://www.wendangku.net/doc/9f9450126.html,Three。但如果using语句声明如下:
using LT = LevelOne.LevelTwo;
namespace LevelOne
{
//name "NameThree" defined
namespace LevelTwo
{
//name "NameThree" defined
}
}
这样全局命名空间中的代码和LevelOne命名空间就可以使用https://www.wendangku.net/doc/9f9450126.html,Three。
这里有一点要注意:using语句本身不能访问令一个命名空间中的名称。除非命名空间中的代码以某种方式链接到项目上,或者代码是在该项目的源文件中定义的,或在链接到该项目的其他代码中定义的,否则就不能访问其中包含的名称。另外,如果包含命名空间的代码链接到项目上,无论是否使用using,都可以访问其中包含的名称。using语句便于我们访问这些名称,减少代码量,使之更合理。
再看看以前我们ConsoleApplication1中的代码,下面的代码被应用到命名空间上:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleAppcation1
{
...
}
using指令块中的3行代码使用using声明:在这段C#代码中使用Systen、
System.Collections.Generic和System.Text命名空间,它们可以在该文件的所有命名空间中访问,无需分类。System命名空间是.NET Framework应用程序的根命名空间,提供了控制台应用程序所需要的所有基本功能。其他两个命名空间常常用于控制台应用程序,所以该程序包含了这3行代码。
最后,为应用程序代码本身声明了一个命名空间ConsoleAppcation1。
流程控制-布尔逻辑
到目前为止,我们看到的C#程序的执行都是一行接一行、自上而下的进行,不遗漏任何代码。如果所有的应用程序都这样执行,那我们能揍的工作就太有限了。
这里介绍控制程序流程的两种方法:
1、分支——有条件的执行代码。条件取决于计算的结果,例如“如果myVal小于0”,就只执行这行代码”。
2、循环——重复执行相同的语句(重复执行一定的次数,或者在满足测试条件后停止执行)。
这两种方法都要用到布尔逻辑。前面介绍的bool类型可以有两个值:true或false。这种类型常常用于记录某些操作的结果,以便操作这些结果。bool类型可用于存储比较过的结果。
考虑下述情形:如果根据变量myVal是否小于10,来确定是否执行代码。为此,需要确定语句“myVal 小于10”的真假,即需要知道比较的布尔结果。
布尔比较需要使用布尔比较运算符(也称为关系运算符),如下所述。这里var1都是bool类型的变量,var2和var3则可以是各种类型。
==:var1=var2==var3;如果var2等于var3,那么var1的值就是true,否则为false。
!=:var1=var2!=var3;如果var2不等于var3,那么var1的值就是true,否则为false。
<:var1=var2 >:var1=var2>var3;如果var2大于var3,那么var1的值就是true,否则为false。 <=:var1=var2<=var3;如果var2大于等于var3,var1的值就是true,否则为false。 >=:var1=var2>=var3;如果var2小于等于var3,var1的值就是true,否则为false。 在代码中,可以对数值使用这些运算符,如下所示: bool isLessThan10; isLessThan10 = myVal < 10; 如果myVal存储的值小于10,这段代码就给isLessThan10赋予true值,否则赋予false值。 也可以对其他类型使用这些比较运算符,例如字符串: bool isKarli; isKarli = myString == "karli"; 如果myString存储的字符串是Karli,isKarli的值就位true,否则为false。 也可以对布尔值使用这些运算符: bool isTrue; isTrue = myBool == true; 但只能使用==和!=运算符。 注意:假定var1 在处理布尔值时,还有其他一些布尔运算符,如下所示: !:var1=!var2;如果var2是false,var1的值就是true,否则为false(逻辑非)。 &:var1=var2&var3;如果var2和var3都是true,var1的值就是true,否则为false(逻辑与)。 |:var1=var2|var3;如果var2或var3是true(或两者都是),var1的值就是true,否则为false(逻辑或)。 ^:var1=var2^var3;如果var2或var3中 有且只有一个是true,var1的值就是true,否则为false(逻辑异或)。 上面的代码也可以表述为: bool isTrue; isTrue = myBool & true; &和|运算符也有两个类似的运算符,称为条件布尔运算符: &&:var1=var2&&var3;如果var2和var3都是true,var1的值就是true,否则为false(逻辑与)。 ||:var1=var2||var3;如果var2或var3是true(或两者都是),var1的值就是true,否则为false(逻辑或)。 这些运算符的结果与&和|完全相同,但得到结果的方式有一个重要区别:其性能比较好。两者都是检查第一个操作数(var2),再根据该操作数的值进行操作,可能根本就不需要第二个操作数的值(var3)。 如果&&运算符的第一个操作数是false,就不需要考虑第二个操作数的值了,因为无论第二个操作数的值是什么,其结果都是false。同样,如果第一个操作数是true,||运算符就返回true,不必考虑第二个操作数的值。 但上面的&和|条件布尔运算符却不是这样。它们的操作数总是要计算的。 因为操作数的计算是有条件的,如果使用&&和||运算符来代替&和|,性能会有一定的提高。在大量使用这些运算符的应用程序中比较明显的。作为一个规则,尽可能使用&&和||运算符。 注意这些运算符有时候用于比较复杂的情形,例如,只有第一个操作数包含某个值时,才计算第二个操作数: var1 = (var2 != 0) && (var3 / var2 >2); 如果var2是0,则var3除以var2就会导致“除0错误”,或者把var1定义位无穷大(对于某些类型如float来说,后则是可能的,也是可以检测到)。 流程控制-布尔逻辑-位运算符 至于为什么会有&和|运算符。是因为这两个运算符可以用于对数值执行操作。实际上,它们处理的是存储在变量中的一系列位,而不是变量的值。 下面先讨论&,第一个操作数中的每个位都与第二个操作数相同位置上的位进行比较,在得到结果中,各个位置上的位如下所示: 操作数1的位:1100 操作数2的位:1010 &的结果位: 1000 |运算符与此类似,但得到的结果是不同的,如下所示: 操作数1的位:1100 操作数2的位:1010 &的结果位: 1110 例如,考虑下面代码中的操作: int result,op1,op2; op1 = 4; op2 = 5; result = op1 & op2; 这里必须考虑op1和op2的二进制表示方式,它们分别是100和101。比较这两个表达方式中相同位置上的二进制数字,得出结果,如下所示: *如果op1和op2最左边的位都是1,result最左边的位就是1,否则为0。 *如果op1和op2次左边的位都是1,result次左边的位就是1,否则为0。 *继续比较其他位。 在这个示例中,op1和op2最左边的位都是1,所以result最左边的位就是1。下一个位都是0,第三个位置上的位分别是1和0,则result第2~3个位都是0。最后,结果的二进制值是100,即结果是4。以下是这个过程: &:100 &4 101 &5 100 &4 如果使用|运算符,将进行相同的过程,但如果操作数中相同位置上的位有一个是1,其结果位就是1,如下所示: |:100 |4 101 |5 101 |5 ^运算符的用法与此相同。如果操作数中相同位置上的位有且只有一个是1,其结果为就是1,如下所示: 操作数1的位:1100 操作数2的位:1010 ^的结果位: 0110 C#中还可以使用一元位运算符~,它将操作数中的位取反,其结果是操作数中位为1的,在结果中就是0,反之亦然: 操作数的位:10 ~的结果位:01 除了这4个位运算符外,还有另外两个运算符,如下所示: >>:var1 = var2>>3 把var2的二进制值向右移动var3位,就得到var1的值。 <<:var1 = var2<<3 把var2的二进制值向左移动var3位,就得到var1的值。 这些运算符通常称为位移运算符,最好举例说明: int var1,var2 = 10,var3 = 2; var1 = var2 << var3; 结果,var1的值是40。具体过程如下:10的二进制是1010,把该数向左移动两位,得到101000,即十进制中的40。实际上,是执行了多个操作。每向左移动一位,该数都要乘以2,所以向左移动两位,就是给原来的操作数乘以4。而每向右移动一位,则是给操作数除以2并丢弃余数。 int var1,var2 = 10; var1 = var2 >> 1; var1的值是5,下下面的代码得到的值是2: int var1,var2 = 10; var1 = var2 >> 2; 流程控制-布尔逻辑-布尔赋值运算符 介绍最后一类运算符是把前面的赋值运算符组合起来,非常类似前面的数学赋值运算符(+=,*=等),如下所示: &=:var1&=var2;var1的值是var1&var2的结果。 |=:var1|=var2;var1的值是var1|var2的结果。 ^=:var1^=var2;var1的值是var1^var2的结果。 这些运算符处理布尔值和数值的方式与&、|和^相同。 注意,&=和|=使用&和|,而不是&&和||,与这些较简单的运算符相关联。 位移运算符也有赋值运算符,如下所示: >>=:var1>>=var2;把var1的二进制值向右移动var2位,就得到var1的值。 <<=:var1<<=var2;把var1的二进制值向左移动var2位,就得到var1的值。 下面看一个例子,让用户输入一个整数,然后代码使用该整数执行各种布尔运算: using System; using System.Collections.Generic; using System.Text; namespace ConsoleApplication4 { class Program { static void Main(string[] args) { Console.WriteLine("请输入一个整数:"); int myInt = Convert.ToInt32(Console.ReadLine());//转换为整数 Console.WriteLine("是否小于10?{0}",myInt < 10); Console.WriteLine("是否大于等于0且小于等于5?{0}",(0 <= myInt && (myInt <= 5))); Console.WriteLine("Bitwise and of Intger and 10 = {0}",myInt & 10); Console.ReadKey(); } } } 前两行使用前面介绍的结束,提示并接受一个整数值: Console.WriteLine("请输入一个数字:"); int myInt = Convert.ToInt32(Console.ReadLine()); 使用Convert.ToInt32()从字符串输入中得到一个整数。Convert.ToInt32()是另一个类型转换命令,与前面使用的Convert.ToDouble()命令术语同系列。 剩下的3行代码对得到的数字进行各种操作,并显示结果。在编写这段代码时,假定用户输入了6:第一个输入的操作是myInt<10的结果。如果myInt是6,则它小于10,因此结果为true。如果myInt的值是10或者更大,就会得到false。 第二个输入设计的计算比较多:(0<=myInt)&&(myInt<=5),其中包含两个比较操作,用于判断myInt的范围。是否大于等于0,且小于等于5.接着对结果进行布尔and操作。输入数字6,则(0<=myInt)返回true,而(myInt<=5)返回false,最终的结果就是(true)&&(false),即false。 流程控制-布尔逻辑-运算符的优先级更新 现在要考虑更多的运算符,所以应更新前面的运算符优先级表,把它们包括在内,优先级由高到低如下所示: ++,--(用作前缀);(),+,-(一元),!,~ *,/,% +,- <<,>> <,>,<=,>= ==,!= & ^ | && || =,*=,/=,%=,+=,-=,<<=,>>=,&=,^=,|= ++,--(用作后缀) 这样增加了好几个级别,但它们明确定义了夏侯苏表达式该如何计算: var1 = var2 <= 4 && var2 >= 2; 其中&&运算符在<=和>=运算符之后执行,可加花括号来使表达式看起码更清晰。 流程控制-goto语句 C#允许给代码加上标签,这样就可以使用goto语句直接跳转到这些代码行上。该语句有其优缺点。优点:这是控制什么时候执行哪些代码的一种非常简单的方式。缺点:过多的使用这个技巧将很难读懂代码。 goto语句用法如下: goto 标签用下述方式定义: 例如,下面的代码: int myInteger = 5; goto myLabel; my Integer += 10; myLabel: Console.WriteLine("myInteger = {0}",myInteger); 其执行过程如下: *myInteger声明位int类型,并赋予值5。 *goto语句中断正常的执行过程,把控制转到标为myLabel:的代码行上。 *myInteger的值写到控制台上。 下面的第3行代码没有被执行。 int myInteger = 5; goto myLabel; myInteger += 10; myLabel: Console.WriteLine("myInteger = {0}",myInteger); 实际上,如果在应用程序中加入这段代码,就会发现编译时,任务列表窗口会显示一个警告,即“Unreachable code detected”和一个行号。 goto语句有它们的用途,但也可能使代码陷入混乱之中。 例如,因使用goto语句而非常难懂的代码如下所示: start: int myInteger = 5; goto addVal; writeResult: Console.WriteLine("myInteger = {0}",myInteger); goto start; addVar: myInteger += 10; goto writeResult; 这是有效的代码,但很难读懂,可以自己试试,看看会发生什么情况。在此之前,应尝试理解这些代码会完成什么任务。