使用变量
现在你已经了解了基本的C#编程,是时候声明一个局部变量了。一旦声明了一个变量,你就可以给他分配一个值,使用新值替换老的,使用它参与运算,输出,等等。然而你不能改变变量的数据类型。在清单1.9中,string max 就是在声明一个变量。
清单1.9 声明和分配变量
————————————————————————————————
————————————————————————————————
局部变量
变量就是代表存储位置的名称,用它可以分配和修改此位置的值。局部表示程序员声明的变量时在方法内部。
确定一个变量的声明,你需要这样做:
- 指定变量将要引用的数据类型
- 分配一个标示符(就是名称)
数据类型
清单1.9中的变量类型是string。在本章还有两个常见的类型是int和char。
- int 是C#指定的整数类型,它长度是32位。
- char用于字符类型。长度是16位,足够Unicode字符集(非扩展)使用
在下一章会有数据类型的细节描述。
什么是数据类型
一个变量所声明数据的类型叫做数据类型(或叫对象类型)。数据类型或简单类型是对具有相似特征或行为的事情分类。比如,动物是一个类型,它是具有所有动物(猴子,猪,鸭嘴兽)特征(多细胞,具有运动能力,等等)的东西。同理,在编程语言中,一个类型就是定义了一些具有天生相似特征的事物。
声明一个变量
清单1.9中,就声明了一个string类型名叫max的变量。也可以在一个语句内一次声明多个特定类型的变量,彼此间用逗号分隔。就像清单1.10演示的这样。
清单1.10 在一个语句内声明两个变量
——————————————————————
string message1,message2
——————————————————————
在C#中,变量名可以是任何字母或以下划线(_)开头,后紧跟任意数量的字母,数字或下划线。作为规范,局部变量名是驼峰风格,并且不包含下划线。
变量的分配
声明一个变量之后,在你引用它前必须分配一个值。可以使用=操作符来做这件事,这也叫简单分配操作符。操作符是用来识别代码的执行功能。清单1.11演示了如何使用分配操作符给变量max,varlerie指定一个string值。
清单1.11 改变变量的值
————————————————————————
class miracleMax
{
static void Main()
{
string valerie;
string max = “Have fun storming the castle!”;
valerie = “Think it will work?”;
System.Console.WriteLine(max);
System.Console.WriteLine(valerie);
max = “It would take a miracle.”;
System.Console.WriteLine(max);
}
}
————————————————————————
观察清单,就可以看出分配变量可以在声明的时候(变量max),也可以再别的语句中分配(就像变量valerie)。分配的值必须在右侧。
输出1.3就是运行编译后的MiracleMax.exe程序代码的展示
输出1.3
>MiracleMax.exe |
C#要求在访问局部变量以前必须分配值。另外,一个赋值可以返回一个值,所以,C#允许在一个语句中两次赋值。就像清单1.12那样。
清单1.12
————————————————————————————
class MiracleMax
{
static void Main()
{
string requirements,max;
requirements = max = “It would take a miracle”;
}
}
————————————————————————————
使用变量
赋值的结果就是你可以使用变量标示符引用其值。所以,当你在System.Console.WriteLine(max)语句中使用max变量时,程序会在控制台上显示变量max值Have fun storming the castle!。当改变max值后并执行同样的显示语句。就是出现max的新值It would table a miracle.
字符串是不可改变的
字符串类型的数据,字符串或其他方式是不可修改的。比如,不可能将字符串"Come As You Are"变为"Come As You Age"。像这样的改变需要你重新分配变量指向一块新的内存,来替代原先的引用。
控制台输入和输出
本章已经多次使用System.Console.WriteLine向命令行输出文本。除了能够输出数据,一个程序还需要一个接收用户数据的入口。
从控制台得到输入
从控制台得到文本的一种方式是使用System.Console.ReadLine()。这个方法中断程序运行以便获得用户录入的字符。当用户敲击回车键,就会产生一个新行。而System.Console.ReadLine()方法将得到一个字符型的文本。来瞅瞅清单1.13和相对应的输出1.4
清单1.13 使用System.Console.ReadLine()
class HeyYou
{
static void Main(string[] args)
{
string firstName;
string lastName;
System.Console.WriteLine("Hey you!");
System.Console.Write("Enter your first name: ");
firstName = System.Console.ReadLine();
System.Console.Write("Enter your last name: ");
lastName = System.Console.ReadLine();
}
}
输出1.4
>HeyYou.exe |
在每个提示符后,程序使用System.Console.ReadLine()方法取得用户录入的文字,并分配给恰当的变量。
到第二个System.Console.ReadLine()赋值完成后,firstName引用的值是Inigo,而lastName引用的值是Montoya
System.Console.Read()
除了System.Console.ReadLine()方法,还有System.Console.Read()方法。不过,后者返回的数据是一个和字符相对应得整数值或如果没有可用字符就是-1。为了取得正确的字符,需要将整数转化成字符。
清单1.14 使用System.Console.Read()
int readValue;
char character;
readValue = System.Console.Read();
character = (char)readValue;
System.Console.Write(character);
System.Console.Read()方法在用户敲击回车键后才能返回值。如果用户在敲击回车前录入多个字符,它依然不会开始处理字符。
向控制台写入输出
在清单1.13中,你提示用户录入他的第一个名字和最后一个名字,在那里你用的是System.Console.Write()方法而不是System.Console.WriteLine()。而不是显示文字之后新起一行录入字符,System.Console.Write()方法停滞在当前行。于是,用户录入的任何字符都在和提升符在同一行。清单1.13就展示了使用System.Console.Write()的输出效果。
下一步就是从控制台使用System.Console.ReadLine()取回的值。就清单1.15来说,程序写下了用户的全名。然而,本代码和从前的相比有一点小小的变化。输出1.5是相应的输出。
清单1.15 使用System.Console.WriteLine()格式化
class HeyYou
{
static void Main(string[] args)
{
string firstName;
string lastName;
System.Console.WriteLine("Hey you!");
System.Console.Write("Enter your first name: ");
firstName = System.Console.ReadLine();
System.Console.Write("Enter your last name: ");
lastName = System.Console.ReadLine();
System.Console.WriteLine("Your full name is {0} {1}.",
firstName, lastName);
}
}
输出1.5
Hey you! |
Your full name is 后跟着firstName的赋值,然后是一个空格,最后就是lastName的值。 清单1.15使用混合格式化输出。对于混合格式化,代码首先提供一个格式化字符。在本例子中,格式字符串是"Your full name is {0}{1}“。它为要插入的数据定义了两个占位符索引。
注意索引值是从零开始的。每个替代参数按顺序和索引值配对。在这个例子中,firstName是紧跟在格式化字符串后的第一个参数,和它相对应的索引值是0 ,同样,lastName就是1了。
注意,占位符是没有顺序的。比如,清单1.6交换了占位符的索引值,并添加了一个逗号。这也将改变名字的现实方式(瞅瞅输出1.6)。
清单1.16
——————————————————————————
System.Console.WriteLine("Your full name is {1},{0}.",
firstName, lastName);
——————————————————————————
输出1.6
Hey you! |
此外,在格式字符串内没有出现连续的占位符,这就可以多次使用同一个占位符。另外,也可以省略占位符。这样的占位符没有对应的参数。
注释
在这一节,需要你修改清单1.15代码添加注释。程序上的这个变化是不会执行的。注释是为了让代码更易懂。清单1.17是这段代码,输出1.7是相应的输出。
清单1.17 代码注释
————————————————————————
————————————————————————
输出1.7
尽管插入了注释,此段新代码编译执行的输出和从前是一样的。
程序员使用注释来描述和解释他们写的代码,尤其是当他自己的写的算法很难懂时,或者也许有一段令人惊讶的特殊算法实现。注释只是供程序员查看代码,the compiler ignores comments and generates an assembly that is devoid of any trace that comments were part of the original source code。
表1.2 展示了C#四种不同的注释风格。
第九章会讨论XML注释,我将会讨论XML标签。
注释类型 |
描述 |
实例 |
分隔注释 |
最前面一个斜杠紧跟着星号,/*这是标示开始注释,末尾用一个星号紧跟着斜杠*/ 。这种方式的注释可以跨越多行或嵌入一行代码中。The asterisks that appear at the beginning of the lines but within the delimiters are simply for formatting | /*注释内容*/ |
单行注释 |
连续两个斜杠,编译器会把斜杠后面的所有文字作为注释。这就是单行注释。 | //注释内容 |
XML分隔注释 |
由/**和**/组成的注释叫做XML分隔注释。它和分隔注释(/**/就是这种方式)没什么区别,除了它可以处理XML内容。编译器能将他们保存到一个文件中。XML分隔注释只能在C#2.0中使用,不过语法是C#1.0的 | /**注释内容**/ |
XML单行注释 |
///是XML单行注释。编译器也能将单行注释存入文件 | ///注释内容 |
扩展标记语言(XML)
XML是一种简单灵活的文本格式常用在Web程序和用来在程序间交换数据。XML是可扩展的因为XML文档中的信息时用来描述数据的,这叫元数据(metadata)。
<?xml version="1.0" encoding="utf-8" ?>
<body>
<book title="Essential C# 3.0">
<chapters>
<chapter title="Introducing C#"/>
<chapter title="Operators and Control Flow"/>
...
</chapters>
</book>
</body>
文件的头标示出XML文件的版本和字符集。之后出现了一个book元素。元素是由一个三角括号开始,就像<body>.为了终止元素,在尖括号内键入同样的单词,并在单词前插入一个斜杠,就想</body>。除了元素,XML还支持属性。titile = “Essential C# 3.0” 就是XML属性。注意元数据(book title,chapter,等等)描述了数据包含在XML文件中。这虽然有可能让结果很臃肿,但这能有助于数据描述和数据解释
托管和通用语言架构
处理器并不能直接解析程序集。程序集是由第二语言组成,这被称为通用中间语言(CLI)或简称IL。
C#编译器将C#源文件翻译成中间语言。这通常是在程序运行时,它需要将CIL代码转化成处理器可以理解的机器码。在C#程序执行时还有一个很重要的东西参与其中:虚拟执行系统(VES)。VES是在运行期编译CIL代码必须的(这个处理过程被称为即时编译)。代码时在代理的上下文中执行,就像运行期的托管代码,并且此处理过程在托管执行运行期的控制下执行。这叫做托管代码,由于被托管的程序行为在运行控制期是很重要部分,比如内存分配,安全,即时编译。非托管代码是不需要运行期的。
C#编译器将C#源文件翻译成中间语言。还有一些额外的步骤,通常在execution time执行时,它必须将CIL代码转化成处理器可以理解的机器码。这就涉及了在C#程序运行时很重要的一个元素:虚拟执行系统(VES)。VES被临时交付给runtime,这是编译CIL代码必须的(这个过程被称作即时编译)。代码时在代理的上下文中执行,就像runtime是托管代码。runtime控制下的执行过程就是托管执行。它之所以被称为托管代码,因为runtime控制了程序行为的很重要部分比如,内存分配,安全,即时编译。非托管代码时不需要runtime的。
注意: 术语runtime是指执行时间或虚拟执行系统。为了清晰,本书使用术语execution time来标识程序正在执行。并且当讨论托管的代理负责C#程序的执行时,使用术语runtime。 |
VES的规范包含在广义概念被称为CLI规范。这是个国家标准,CLI包括的规范有:
- VES或runtime
- CIL
- 支持语言交换的类型系统,这被称为通用类型系统(CTS)
- 指导如何书写能访问CLI的库(相应的说明在通用语言规范(CLS))
- 能被CLI识别的元数据
- 一个通用编程架构,基本类库(BCL)
在CLI实现的上下文中运行能够执行许多服务和特征,程序员不需要直接编写下面的东西。
- 语言交互性:在两个语言的交互能力。语言编译器将各自的语言都翻译成中间语言(CIL)
- 类型安全:检查类型间的转化。这帮助预防发生缓冲区溢出,这是安全攻击的原因
- 代码访问安全:运行程序员的代码有权利在机器上执行
- 垃圾回收:为在runtime分配的数据空间自动管理内存
- 移动平台:支持将同一个程序集运行在多种操作系统上。但就不能使用和平台相关的类库了。就像JAVA。
- BCL:提供一个开发者能依赖的庞大的基础代码(这些都是CLI的实现)。
C#和.Net版本
输出1.1显示“.Net Framework version 3.5”。在写本书的时候,微软发行了四个.NetFramework版本,而只有三个C#编译器发行。 .NET Framework version 3.0 was an additional set of API libraries released betweenC# compiler releases (and Visual Studio 2005 and 2008 versions)。对于结果,C#3.0相当于Framework 3.5。尽管C#3.0中的一些功能可以再Framework 2.0/3.0中使用,但不包含一些重要的特征比如LINQ。表1.3是对C#和.Net发行的描述。
表1.3 C#和.net版本
解释类型 | 描述 |
Framework1.0/1.1中的C#1.0(VS2002和2003) | 这是C#的最初版本,用来支持.Net编程 |
Framework2.0中的C#2.0(VS2005) | 泛型被添加到此语言中。并且Framework2.0也开始支持泛型 |
Framework3.0 | 添加了一组API库WCF,WPF,WF。和Web证书(Cardspaces) |
Framework3.0中的C#3.0(VS2008) | 支持LinQ。 |
通用中间语言和ILDASM
上一节提到,C#编译器将C#代码转化成CIL代码而不是机器码。处理器能理解机器码,而明白CIL代码。这就需要在处理器执行程序以前将其转换。对于一个程序集(DLL或可执行文件),可以使用CIL反汇编工具查看CIL代码。此程序将反编译一个代码或一个类库。。。。。。。。。。。。。。。。。。。。。。。。。。
后面还有一点,大概有三四百字吧,实在没心情搞了,回头再说。