您的位置:首页 > 编程语言 > C#

C#语言参考--(1)介绍-1

2007-06-05 14:00 369 查看
1. 介绍
C#是一种简单、现代、面向对象和类型安全的编程语言,由C和C++发展而来。C#(发音为“C霎普”)牢固地植根于C和C++语言族谱中,并且会很快被C和C++程序员所熟悉。C#的目标在于把Visual Basic的高生产力和C++本身的能力结合起来。
C#作为Microsoft Visual Studio 7.0的一部分提供给用户。除了C#以外,Visual Studio还支持Visual Basic、Visual C++和描述语言VBScript和Jscript。所有这些语言都提供对Microsoft .NET平台的访问能力,它包括一个通用的执行引擎和一个丰富的类库。Microsoft .NET平台定义了一个“通用语言子集”(CLS),是一种混合语言,它可以增强CLS兼容语言和类库间的无缝协同工作能力。对于C#开发者,这意味着既是C#是一种新的语言,它已经可以对用老牌工具如Visual Basic和Visual C++使用的丰富类库进行完全访问。C#自己并没有包含一个类库。
本章的其余部分描述这种语言的基本特性。以后的章节将会详细描述规则和例外,并且有些时候以数学的方式描述,而这章会致力于对整体的简单而清楚地介绍。这样的目的是给读者一个关于语言的介绍,这样可以使读者可以更容易地开始编写程序和继续阅读后面的章节。
1.1 Hello, world
规范的“Hello,World”程序可以按照下面例子编写:
using System;
class Hello
{
static void Main() {
Console.WriteLine("Hello, world");
}
}
C#程序的源代通常存储在一个或多个扩展名为.cs的文件中,例如hello.cs。如果使用Visual Studio提供的命令行编译器,这样的程序可以用命令行命令来编译
csc hello.cs
这样就会生成一个名为hello.exe的可执行程序。程序的输出如下:
Hello, world
下面对这个程序进行详细的研究:
• 使用System;指令涉及到一个名称空间(namespace)叫作System,这是在Microsoft .NET类库中提供的。这个名称空间包括在Mian方法中使用的Console类。名称空间提供了一种用来组织一个类库的分层方法。使用“using”命令后,就可以无障碍地使用名称空间中的各种类型成员。“Hello,world”程序中使用的Console.WriteLine是System.Console.WriteLine的简写。
• Main方法是类Hello中的一个成员,它有static的说明符,所以它是类Hello中的一个方法而不是此类中的实例。
• 对于一个应用程序的主入口点-称开始执行的方法-通常是一个称为Main的静态方法。
• “Hello,world”的输出是通过使用类库产生的。语言本身并没有提供类库。作为替代,它使用一个通用类库,这个类库也可以被诸如Visual Basic和Visual C++的语言所使用。
对于C和C++开发者来说,会有兴趣知道对一些没有出现在“Hello,world”程序的东西。
• 程序没有把Main设为全局方法。在全局级别上不支持方法和变量;这些元素通常包含在类型声明当中(例如,类或结构的声明)。
• 程序中不使用“::”或“->”操作符。“::”不再是一个操作符,而“->”操作符也只是在程序的某个小片断中才会使用。操作符“.”用于符合名称,例如Console.WriteLine。
• 程序中不包括前向声明。因为声明的顺序不重要,所以不再需要前向声明。
• 程序中不使用#include关键字。程序中的从属关系是象征性的而不是字面上地。这个系统消除了在用不同语言编写的程序间的障碍。例如,Console类可以用另外一种语言编写。
1.2 类型
C#支持两种类型:数据类型和引用类型。数据类型包括一些简单类型(例如,char、int和float),枚举类型和结构类型。引用类型包括类类型、接口类型、代表(delegate)类型和数组类型。
数据类型和引用类型的区别在于,数据类型变量直接包含它们的数据,然而引用类型数据是存储对于对象的引用。对于引用类型,有可能两个变量引用相同的对象,因而可能出现对一个变量的操作影响到其它变量所引用对象的情况。对于数据类型,每个变量都有它们自己对数据的拷贝,所以不太可能因为对一个进行操作而影响到其它变量。
例子
using System;
class Class1
{
public int value = 0;
}
class Test
{
static void Main() {
int val1 = 0;
int val2 = val1;
val2 = 123;
Class1 ref1 = new Class1();
Class1 ref2 = ref1;
ref2.value = 123;
Console.WriteLine("values: {0}, {1}", val1, val2);
Console.WriteLine("Refs: {0}, {1}", ref1.value, ref2.value);
}
}
从此例可以说明两者间的不同。程序的输出如下
values: 0, 123
Refs: 123, 123
对局部变量val1的赋值没有影响到局部变量val2,因为两个局部变量都是数据类型(int类型),并且每个数据类型的局部变量都有它们自己的存储。与此相对的是,对于ref.value的赋值ref.value=123对ref1和ref2都有影响。
这两行
Console.WriteLine("values: {0}, {1}", val1, val2);
Console.WriteLine("Refs: {0}, {1}", ref1.value, ref2.value);
值得更多的注释,因为它们可以让我们看到Console.WriteLine的一些字符串格式输出方式,这些实际上是使用了一些变量。第一个变量是一个字符串,它包含一些数字的占位符如{0}和{1}。每个占位符指向一个变量。占位符{0}指向第二个变量,占位符{1}指向第三个变量,等等。在输出被送到控制台前,这些占位符会被它们相对应的变量所替换。
开发者可以通过枚举和结构声明定义新数据类型,可以通过类、接口和代表声明来定义新引用类型。例子
using System;
public enum Color
{
Red, Blue, Green
}
public struct Point
{
public int x, y;
}
public interface IBase
{
void F();
}
public interface IDerived: IBase
{
void G();
}
public class A
{
protected void H() {
Console.WriteLine("A.H");
}
}
public class B: A, IDerived
{
public void F() {
Console.WriteLine("B.F, implementation of IDerived.F");
}
public void G() {
Console.WriteLine("B.G, implementation of IDerived.G");
}
override protected void H() {
Console.WriteLine("B.H, override of A.H");
}
}
public delegate void EmptyDelegate();
这里用一两个例子说明每种类型声明,以后的章节里会更详细地描述类型声明。
1.2.1 预定义类型
C#提供了一系列预定义类型,其中大多数对C和C++程序员来说都是比较熟悉的。
预定义引用类型是对象和字符串。类型对象是所有其它类型的最根本的基础类型,而类型字符串要用来说明Unicode字符串数据。
预定义数据类型包括有符号和无符号整数类型、浮点数类型、二进制、字符和十进制类型。有符号整数类型有sbyte、short、int和long;无符号整数类型有byte、ushort、uint和ulong;而浮点类型有float和double。
二进制类型用来表示二进制数据:值或者是真或者是假。包含二进制使得编写自说明代码变得容易,并且也帮助消除所有由于程序员在应当使用“==”时错误的使用了“=”造成的很普通的C++代码错误。在C#中,下面的例子
int i = ...;
F(i);
if (i = 0) // Bug: the test should be (i == 0)
G();
是非法的,因为表达式i=0的类型是int,而if声明需要一个二进制类型的表达式。
Char类型用来说明Unicode字符。某个char类型变量说明一个单16位Unicode字符。
十进制类型适合应用在不能接受舍入误差计算中。通常的例子包括商业计算,例如税收计算和货币转换。十进制类型提供了28个有效位。
下面的表中列出了预定义类型,并且指出了如何为每一个类型赋值。
类型 描述 例子
object 所有其它类型的最根本的基础类型 object o = null;
string 字符串类型;一个字符传是一个Unicode字符序列 string s = "Hello";
sbyte 8-bit 有符号整数类型 sbyte val = 12;
short 16-bit有符号整数类型 short val = 12;
int 32-bit 有符号整数类型 int val = 12;
long 64-bit有符号整数类型 long val1 = 12;
long val2 = 34L;
byte 8-bit 无符号整数类型 byte val1 = 12;
byte val2 = 34U;
ushort 16-bit无符号整数类型 ushort val1 = 12;
ushort val2 = 34U;
uint 32-bit无符号整数类型 uint val1 = 12;
uint val2 = 34U;
ulong 64-bit无符号整数类型 ulong val1 = 12;
ulong val2 = 34U;
ulong val3 = 56L;
ulong val4 = 78UL;
float 单精度浮点数类型 float val = 1.23F;
double 双精度浮点数类型 double val1 = 1.23;
double val2 = 4.56D;
bool 二进制类型; 一个二进制数据不是真就是假 bool val1 = true;
bool val2 = false;
char 字符类型; 一个字符数据是一个Unicode字符 char val = ’h’;
decimal 精确十进制类型,有28个有效位 decimal val = 1.23M;

每一个预定义类型都是某个系统提供的类型的简写。例如,关键词int是一个名为System.Int32结构的简写。虽然更好的考虑是使用关键词而不是完全的系统类型名称,但是这两个名称可以交换使用。
例如int的预定义数据类型在某些地方被认为是特别的,但在大多数地方会像其它结构一样被正确对待。操作符重载使得编程人员可以定义同预定义数据类型行为相同的类型。例如,一个Digit结构可以支持与整数类型相同的数学操作,并且可以定义Digit和预定义类型间的转换。
预定义类型可以允许操作符重载它们自己。例如,比较符==和!=对应不同的预定义类型有不同的语意:
• 如果两个int类型的表达式代表了相同的整数据,它们被认为是相等的。
• 如果两个object类型的表达式都指向相同的对象或者都是空的,它们被认为是相等的。
• 如果字符串实例有相同的长度并且在每个字符的位置都相同,或者都为空,这两个字符串类型的表达式就被认为是相等的。
例子
class Test
{
static void Main() {
string s = "Test";
string t = string.Copy(s);
Console.WriteLine(s == t);
Console.WriteLine((object)s == (object)t);
}
}
产生下面的输出
True
False
因为第一个比较符比较两个string类型的表达式,而第二个比较符比较两个object类型的表达式。
1.2.2 转换
有两种类型的转换隐式转换和显式转换。隐式转换应用于需要小心地仔细检查就可以安全地实现的转换。例如,从int到long就是一个隐式转换。隐式转换通常都能成功,并且不会带来失去信息的后果。就像例子中所示,隐式转换可以隐式地实现。
using System;
class Test
{
static void Main() {
int intvalue = 123;
long longvalue = intvalue;
Console.WriteLine("(long) {0} = {1}", intvalue, longvalue);
}
}
这里隐式地把int转换为long。
相反,显式转换必须要个安排好的表达式才能实现。例子
using System;
class Test
{
static void Main() {
long longvalue = Int64.Maxvalue;
int intvalue = (int) longvalue;
Console.WriteLine("(int) {0} = {1}", longvalue, intvalue);
}
}
用显式转换把long转换到int。输出为:
(int) 9223372036854775807 = -1
因为发生溢出。
1.2.3 数组类型
数组可以是一维或多维。支持“矩形”数组也支持“不规则”数组。
一维数组是最普通的类型,所以,从这里开始讲是个不错的开始。例子
using System;
class Test
{
static void Main() {
int[] arr = new int[5];
for (int i = 0; i < arr.Length; i++)
arr[i] = i * i;
for (int i = 0; i < arr.Length; i++)
Console.WriteLine("arr[{0}] = {1}", i, arr[i]);
}
}
创建一个一维int数据数组,初始化数组元素并且把每个元素打印出来。程序的输出为:
arr[0] = 0
arr[1] = 1
arr[2] = 4
arr[3] = 9
arr[4] = 16
前面例子中使用的int[]类型是一个数组类型。数组类型的写法是前面一个非数组类型,其后跟一个或多对中括号。例子
class Test
{
static void Main() {
int[] a1; // single-dimensional array of int
int[,] a2; // 2-dimensional array of int
int[,,] a3; // 3-dimensional array of int
int[][] j2; // "jagged" array: array of (array of int)
int[][][] j3; // array of (array of (array of int))
}
}
介绍了各种使用int类型元素的数组类型定义局部变量的方法。
数组是引用类型,所以声明一个数组变量只是为对此数组的引用设置了空间。数组实例的实际创建是通过数组初始化程序和数组创建表达式。例子
class Test
{
static void Main() {
int[] a1 = new int[] {1, 2, 3};
int[,] a2 = new int[,] {{1, 2, 3}, {4, 5, 6}};
int[,,] a3 = new int[10, 20, 30];
int[][] j2 = new int[3][];
j2[0] = new int[] {1, 2, 3};
j2[1] = new int[] {1, 2, 3, 4, 5, 6};
j2[2] = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9};
}
}
给出了各种数组创建表达式.变量a1、a2和a3表示矩形数组,而 变量j2表示了一个不规则数组。这里没必要对在这里按照数组的形状分类感到惊讶。矩形数组通常有矩形的形状,给出数组每一维的长度,它的矩形形状就清楚了。例如,a3的三维长度分别为10、20和30,并且不难看出这个数组包括10*20*30个元素。
相反,变量j2表示一个“不规则”数组,或者称为“数组的数组”。特别是,j2表示了一个int数组组成的数组,或者说是一个一维int[]类型的数组。这些int[]变量中的每一个都可以独自被初始化,同时允许数组有一个不规则形状。例子中为每个int[]数组定义了不同的长度。很清楚地可以看到,j2[0]的长度是3,j2[1]的长度是6,j2[2]的长度是9。
元素的类型和数组的维数是一个数组类型的一部分,但每一维的长度不是。这项不同从语法上看更清楚,每一维的长度是在数组创建表达式中指出而不是在数组类型中。例如下面的声明
int[,,] a3 = new int[10, 20, 30];
为int[,,]类型的数组和一个数组创建表达式new int[10,20,30]。
对于局部变量和域声明,允许一种简写形式,这样就不需要去再声明数组类型。例如,下面的例子
int[] a1 = new int[] {1, 2, 3};
可以简化为
int[] a1 = {1, 2, 3};
程序语意没有任何变化。
一个数组初始化程序的上下文,例如{1,2,3},被用来决定数组被初始化的类型。例子
class Test
{
static void Main() {
short[] a = {1, 2, 3};
int[] b = {1, 2, 3};
long[] c = {1, 2, 3};
}
}
说明相同的数组初始化程序可以用于许多不同数组类型的初始化。因为需要上下文来决定一个数组初始化程序的类型,所以不可能在表达式上下文中使用数组初始化程序。
1.2.4 类型系统一致
C# 提供了“统一类型系统”。包括数据类型的所有类型都从object类型派生出来。可以从任何数据调用object的方法,甚至像int这样“简单的”类型的数据。例子
using System;
class Test
{
static void Main() {
Console.WriteLine(3.ToString());
}
}
用一个整数数据符号来调用在object中定义的ToString方法。
下面这个例子
class Test
{
static void Main() {
int i = 123;
object o = i; // boxing
int j = (int) o; // unboxing
}
}
更加有趣。一个int数据可以被转换为object并且还可以转换回int类型。这个例子说明了boxing和unboxing当一个数据类型变量需要转换为引用类型时,一个名为box的对象被分配来保存数据,并且数据被拷贝到box中。Unboxing恰恰相反。当一个对象box要恢复到它原始的数据类型的时候,数据被从对象box中拷贝到适当的存储位置。
类型系统一致提供了有对象性质的数值,而不用引入不需要的开支。在不需要int数值表现得像对象的程序中,int数值只是简单的32位数值。对于需要int数值的行为像一个对象的程序,这项能力是可以实现的。这个能力把数值类型当做大多数语言中存在的数值类型和引用类型间的桥梁。例如类Stack可以提供Push和Pop方法,并返回一个object数值。

public class Stack
{
public object Pop() {...}
public void Push(object o) {...}
}
因为C#有统一类型系统,Stack类可以用来为任何类型数据生成堆栈,包括像int这样的数据类型。
1.3 变量和参数
变量扮演存储的角色。每个变量有一个类型,这个类型决定那些数据可以被存储在这个变量中。局部变量是在方法、属性或索引中声明的变量。一个局部变量通常通过指定的类型名称和说明符来定义,它指定了变量名称和一个任意的初始值,如下:
int a;
int b = 1;
但也有可能一个局部变量声明包括多个说明符。对于a和b的声明可以写成:
int a, b = 1;
一个变量在它的数据可以使用前,必须被明确分配数据(§错误!未找到引用源。)。例子
class Test
{
static void Main() {
int a;
int b = 1;
int c = a + b;
...
}
}
是非法的,因为试图在一个变量被分配数据前就试图使用它。
域 (§10.4) 是一种变量,它与某个类或结构或者某个类或结构的实例相关联。一个用static修饰符声明的域定义了一个静态变量,而不用这种修饰符声明的域定义一个实例变量。例子
using System.Data;
class Employee
{
private static DataSet ds;
public string Name;
public decimal Salary;
...
}
介绍了Employee类,它有一个私有静态变量和两个公用实例变量。
形式参数声明同样定义变量。这里有四种类型的参数:数据参数,引用参数,输出参数和参量(param)参数。
数据参数用来做“入”参数传递,一个自变量的数据通过它传递到方法中,而对参数的修改不会影响到原始的自变量。数据参数指向它自己在存储器中的位置,它与变量存储位置有明确的区分。次存储位置通过把拷贝相应变量的数据来初始化。例子
using System;
class Test {
static void F(int p) {
Console.WriteLine("p = {0}", p);
p++;
}
static void Main() {
int a = 1;
Console.WriteLine("pre: a = {0}", a);
F(a);
Console.WriteLine("post: a = {0}", a);
}
}
说明了一个方法F,它有一个名为p的数据参数。这个例子产生下面的输出:
pre: a = 1
p = 1
post: a = 1
甚至数据p被改动。
引用参数是用作“通过引用”参数传递,这里,参数表现为调用者提供变量的别名。引用参数自己并不定义存储位置,而是指向相应变量的存储位置。对引用参数的修改马上会直接地影响到相应的变量。引用参数用一个ref修饰符来声明。例子
using System;
class Test {
static void Swap(ref int a, ref int b) {
int t = a;
a = b;
b = t;
}
static void Main() {
int x = 1;
int y = 2;

Console.WriteLine("pre: x = {0}, y = {1}", x, y);
Swap(ref x, ref y);
Console.WriteLine("post: x = {0}, y = {1}", x, y);
}
}
说明了有两个引用参数的方法Swap。程序的输出如下:
pre: x = 1, y = 2
post: x = 2, y = 1
关键词ref必须在形式参数中声明并且在其中使用。在call位置使用ref要求对参数特殊注意,这样,一个开发人员在阅读此段代码的时候就可以理解到,由于此调用变量将发生变化。
除了调用者所提供变量的初始化数据不重要以外,输出参数与引用参数相似。用一个out修饰符来声明一个输出参数。例子
using System;
class Test {
static void Divide(int a, int b, out int result, out int remainder) {
result = a / b;
remainder = a % b;
}
static void Main() {
for (int i = 1; i < 10; i++)
for (int j = 1; j < 10; j++) {
int ans, r;
Divide(i, j, out ans, out r);
Console.WriteLine("{0} / {1} = {2}r{3}", i, j, ans, r);
}
}
}
介绍了一个包括两个输出参数的Divide方法,一个是除的结果,另外一个是余数。
对于数据,引用和输出参数在调用者提供的变量和代表它们的参数间有一个一一对应的关系。参量参数可以可以允许多对一的关系:多个变量可以由一个参量参数来代表。换句话说参量参数可以接受长度变化的变量列表。
参量参数用一个params修饰符来声明。对于一个给定的方法,只能有一个参量参数,并且通常指定为最后一个参数。参量参数通常是一维数组类型。调用程序可以只是传送这种数组类型的一个单独的变量,也可以是这种数组类型中,于数组元素类型相同的任意多个变量。例如,下面的例子
using System;
class Test
{
static void F(params int[] args) {
Console.WriteLine("# of arguments: {0}", args.Length);
for (int i = 0; i < args.Length; i++)
Console.WriteLine("/targs[{0}] = {1}", i, args[i]);
}
static void Main() {
F();
F(1);
F(1, 2);
F(1, 2, 3);
F(new int[] {1, 2, 3, 4});
}
}
介绍了一个F方法,它有可变数量的int变量和许多对这个方法的调用。输出是:
# of arguments: 0
# of arguments: 1
args[0] = 1
# of arguments: 2
args[0] = 1
args[1] = 2
# of arguments: 3
args[0] = 1
args[1] = 2
args[2] = 3
# of arguments: 4
args[0] = 1
args[1] = 2
args[2] = 3
args[3] = 4
在介绍中出现的大部分例子都使用Console类中的Writeline方法。如例子中完全用参量参数进行变量替换。
int a = 1, b = 2;
Console.WriteLine("a = {0}, b = {1}", a, b);
WriteLine方法提供了多种传递少量变量的方法,而其中一种使用了参量参数。
namespace System
{
public class Console
{
public static void WriteLine(string s) {...}
public static void WriteLine(string s, object a) {...}
public static void WriteLine(string s, object a, object b) {...}
...
public static void WriteLine(string s, params object[] args) {...}
}
}
1.4 自动内存管理
手工内存管理需要开发者管理内存块的分配和重新分配。手工管理内存既耗时又困难。提供自动内存管理可以使开发者从繁重的任务中解放出来。在大多数情况下,自动内存管理可以在没有反面影响表现和性能的情况下增加代码的质量,提高开发者生产力。
例子
using System;
public class Stack
{
private Node first = null;
public bool Empty {
get {
return (first == null);
}
}
public object Pop() {
if (first == null)
throw new Exception("Can’t Pop from an empty Stack.");
else {
object temp = first.value;
first = first.Next;
return temp;
}
}
public void Push(object o) {
first = new Node(o, first);
}
class Node
{
public Node Next;
public object value;
public Node(object value): this(value, null) {}
public Node(object value, Node next) {
Next = next;
value = value;
}
}
}
介绍了一个作为Node实例链接表执行类Stack。Node实例在Push方法中创建,当不再需要时被碎片收集。当其它程序没有任何可能去访问Node实例时,它就符合碎片收集的条件了。例如当一个项目被从Stack中移走,相关的Node实例就变为符合碎片收集的条件。
例子
class Test
{
static void Main() {
Stack s = new Stack();
for (int i = 0; i < 10; i++)
s.Push(i);
s = null;
}
}
介绍了一个使用Stack类的测试程序。Stack被创建并且初始化为包含10个元素,然后被赋值为数据null。一旦变量s被赋值为null后,Stack和相应的10个Node实例就变成符合碎片收集的条件了。碎片收集程序马上就被允许进行清理,虽然还不需要这样做。
对于一个通常是使用自动内存管理,但有时需要精细控制或希望有些许性能提高的程序员来说,C#提供了编写“非安全”代码的能力。这样的代码可以由指针类型直接处理,而且fix对像可以暂时保护这些代码,防止被碎片收集程序收集。从开发者和用户角度来看,这个“非安全”代码属性实际上是“安全”属性。非安全代码必须用unsafe修饰符明确标明,这样开发者就不会在偶然的情况下使用这项非安全属性,并且编译器和执行程序一起来保证非安全代码不会伪装为安全代码。
例子
using System;
class Test
{
unsafe static void WriteLocations(byte[] arr) {
fixed (byte *p_arr = arr) {
byte *p_elem = p_arr;
for (int i = 0; i < arr.Length; i++) {
byte value = *p_elem;
string addr = int.Format((int) p_elem, "X");
Console.WriteLine("arr[{0}] at 0x{1} is {2}", i, addr, value);
p_elem++;
}
}
}
static void Main() {
byte[] arr = new byte[] {1, 2, 3, 4, 5};
WriteLocations(arr);
}
}
介绍了名为WriteLocations的非安全方法,它选定一个数组实例并且用指针反复对元素进行操作。每个数组元素的标号,数据和位置写到控制台。这个程序的一个可能的输出为:
arr[0] at 0x8E0360 is 1
arr[1] at 0x8E0361 is 2
arr[2] at 0x8E0362 is 3
arr[3] at 0x8E0363 is 4
arr[4] at 0x8E0364 is 5
但是,准确的存储位置肯定要发生变化。
1.5 表达式
C# 包括一元操作符,二元操作符和一个三元操作符。在下面的表中,对操作符进行了总结,按照从最高到最低的优先顺序列出这些操作符:
Section Category Operators
0
基本的 (x) x.y f(x) a[x] x++ x-- new
typeof sizeof checked unchecked
错误!未找到引用源。
一元的 + - ! ~ ++x --x (T)x
错误!未找到引用源。
乘法的 * / %
错误!未找到引用源。
加法的 + -
错误!未找到引用源。
移位 << >>
错误!未找到引用源。
关系 < > <= >= is
错误!未找到引用源。
等式 == !=
错误!未找到引用源。
逻辑与 &
错误!未找到引用源。
逻辑异或 ^
错误!未找到引用源。
逻辑或 |
错误!未找到引用源。
条件与 &&
错误!未找到引用源。
条件或 ||
错误!未找到引用源。
条件的 ?:
错误!未找到引用源。
赋值 = *= /= %= += -= <<= >>= &= ^= |=

当一个表达式包括几个操作符,操作符的优先级控制顺序,这里对每个单独操作符进行等效。例如,因为*操作符的优先级比+操作符高,所以表达式x+y*z等效为x+(y*z)。
当一个操作数出现在两个有相同优先级的操作符之间时,操作符的传递关系控制操作实现的顺序:
• 除了赋值操作符,所有二元操作符是左向传递,意味着操作是从左向右进行。例如,x+y+z等效为(x+y)+z。
• 赋值操作符和条件操作符是右向传递的,意味着操作是从右向左进行。例如,x=y=z等效为x=(y=z)。
使用括号可以控制优先级和传递关系。例如,x+y*z先把y和z相乘,然后把结果同x相加。但是(x+y)*z首先把x和y相加,然后把结果乘以z。
1.6 声明
虽然有些值得注意的增加和修改,C# 的大多数声明都是从C和C++继承的。下面的表列出了可用的声明的类型,并且为每个提供了例子。
声明 例子
声明列表和块声明 static void Main() {
F();
G();
{
H();
I();
}
}
标号声明和goto声明 static void Main(string[] args) {
if (args.Length == 0)
goto done:
Console.WriteLine(args.Length);

done:
Console.WriteLine("Done");
}
局部常量声明 static void Main() {
const float pi = 3.14;
const int r = 123;
Console.WriteLine(pi * r * r);
}
局部变量声明 static void Main() {
int a;
int b = 2, c = 3;
a = 1;
Console.WriteLine(a + b + c);
}
表达式声明 static int F(int a, int b) {
return a + b;
}
static void Main() {
F(1, 2); // Expression statement
}
If声明 static void Main(string[] args) {
if (args.Length == 0)
Console.WriteLine("No args");
else
Console.WriteLine("Args");
}
Switch声明 static void Main(string[] args) {
switch (args.Length) {
case 0:
Console.WriteLine("No args");
break;
case 1:
Console.WriteLine("One arg ");
break;
default:
int n = args.Length;
Console.WriteLine("{0} args", n);
break;
}
}
While声明 static void Main(string[] args) {
int i = 0;
while (i < args.length) {
Console.WriteLine(args[i]);
i++;
}
}
do声明 static void Main() {
string s;
do { s = Console.ReadLine(); }
while (s != "Exit");
}
for声明 static void Main(string[] args) {
for (int i = 0; i < args.length; i++)
Console.WriteLine(args[i]);
}
Foreach 声明 static void Main(string[] args) {
foreach (string s in args)
Console.WriteLine(s);
}
Break 声明 static void Main(string[] args) {
int i = 0;
while (true) {
if (i > args.Length)
break;
Console.WriteLine(args[i++]);
}
}
continue 声明 static void Main(string[] args) {
int i = 0;
while (true) {
Console.WriteLine(args[i++]);
if (i > args.Length)
continue;
break;
}
}
return 声明 static int F(int a, int b) {
return a + b;
}
static void Main() {
Console.WriteLine(F(1, 2));
return;
}
throw 声明 and try 声明 static int F(int a, int b) {
if (b == 0)
throw new Exception("Divide by zero");
return a / b;
}
static void Main() {
try {
Console.WriteLine(F(5, 0));
}
catch(Exception e) {
Console.WriteLine("Error");
}
}
checked 和 unchecked 声明 static void Main() {
int x = 100000, y = 100000;
Console.WriteLine(unchecked(x * y));
Console.WriteLine(checked(x * y)); // Error
Console.WriteLine(x * y); // Error
}
lock 声明 static void Main() {
A a = ...
lock(a) {
a.P = a.P + 1;
}
}

问题
我们要在这里添加一些章节说明C#与C和C++不同的地方。这些应该是:
• Goto约束
• 不是所有的表达式都可用被用作声明
• if, while, 和 do声明需要二进制表达式
• 对于switch声明没有失败
• foreach 声明
• 例外处理
• Checked 和 unchecked 声明
• Lock 声明
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: