博文
C# 语言规范--1.12 命名空间和程序集(2006-09-03 18:32:00)
摘要:除了依赖于几个系统提供的类(如 System.Console),到目前为止介绍的程序都是独立存在的。但更常见的情况是:实际的应用程序由若干不同的部分组成,每个部分分别进行编译。例如,企业级应用程序可能依赖于若干不同的组件,其中包括某些内部开发的组件和某些从独立软件供应商处购买的组件。
命名空间和程序集有助于开发基于组件的系统。命名空间提供一个逻辑组织体系。命名空间既用作程序的“内部”组织体系,也用作“外部”组织体系(一种表示向其他程序公开程序元素的途径)。
程序集用于物理打包和部署。程序集可以包含类型、用于实现这些类型的可执行代码以及对其他程序集的引用。
有两种主要的程序集:应用程序和库。应用程序有一个主入口点,通常具有 .exe 文件扩展名;而库没有主入口点,通常具有 .dll 文件扩展名。
为了说明命名空间和程序集的使用,本节再次以前面介绍的“hello, world”程序为例,并将它分为两个部分:提供消息的类库和显示消息的控制台应用程序。
这个类库仅含一个名为 HelloMessage 的类。示例// HelloLibrary.cs
namespace Microsoft.CSharp.Introduction
{
public class HelloMessage
{
public string Message {
get {
return "hello, world";
}
}
}
}
显示了名为 Microsoft.CSharp.Introduction 的命名空间中的 HelloMessage 类。HelloMessage 类提供一个名为 Message 的只读属性。命名空间可以嵌套,而声明namespace Microsoft.CSharp.Introduction
{...}
仅是若干层命名空间嵌套的简写形式。若不简化,则应该像下面这样声明:namespace Microsoft
{
namespace CSharp
{
namespace Introduction
{...}
}
}
将“hello, world”组件化的下一个步骤是编写使用 Hel......
C# 语言规范--1.11 枚举(2006-09-03 18:32:00)
摘要:枚举类型声明为一组相关的符号常数定义了一个类型名称。枚举用于“多项选择”场合,就是程序运行时从编译时已经设定的固定数目的“选择”中做出决定。
示例enum Color
{
Red,
Blue,
Green
}
class Shape
{
public void Fill(Color color) {
switch(color) {
case Color.Red:
...
break;
case Color.Blue:
...
break;
case Color.Green:
...
break;
default:
break;
}
}
}
显示了一个 Color 枚举和一个使用此枚举的方法。Fill 方法的签名清楚地表明可以用给定的颜色之一来填充形状。
使用枚举胜过使用整数常数(在没有枚举的语言中很常见),这是因为使用枚举使代码更具可读性和自归档。代码的自归档特性还使开发工具可以帮助代码编写和其他“设计器”活动。例如,对参数类型使用 Color 而不用 int 使聪明的代码编辑器能够建议 Color 值。
......
C# 语言规范--1.10 委托(2006-09-03 18:31:00)
摘要:委托适用于那种在某些其他语言中需用函数指针来解决的情况(场合)。但是,与函数指针不同,委托是面向对象和类型安全的。
委托声明定义一个类,它是从 System.Delegate 类派生的类。委托实例封装了一个调用列表,该列表列出了一个或多个方法,每个方法称为一个可调用实体。对于实例方法,可调用实体由一个实例和该实例的方法组成。对于静态方法,可调用实体仅由一个方法组成。如果用一组合适的参数来调用一个委托实例,则该委托实例所封装的每个可调用实体都会被调用,并且用的都是上述的同一组参数。
委托实例的一个有趣且有用的属性是:它既不知道也不关心有关它所封装的方法所属的类的种种详情;对它来说最重要的是这些方法与该委托的类型兼容。这使委托非常适合“匿名”调用。这是一个强大的功能。
定义和使用委托分三个步骤:声明、实例化和调用。委托用委托声明语法来声明。示例delegate void SimpleDelegate();
声明一个名为 SimpleDelegate 的委托,它不带参数并且不返回任何结果。
示例class Test
{
static void F() {
System.Console.WriteLine("Test.F");
}
static void Main() {
SimpleDelegate d = new SimpleDelegate(F);
d();
}
}
创建一个 SimpleDelegate 实例,然后立即调用它。
这样做(为方法创建一个委托,然后立即通过这个委托调用该方法)并没有太大意义,因为直接调用方法更简单。当涉及它的匿名特性时,委托才真正显示出它的效用。示例void MultiCall(SimpleDelegate d, int count) {
for (int i = 0; i < count; i++) {
d();
}
}
显示了一个重复调用 SimpleDelegate 的 MultiCall 方法。MultiCall 方法不知道也不在乎 SimpleDelegate 的目标方法的类型、该方法所具有的可访问性或者该方法是否为静态。对它来说最重要的是目标方法与 SimpleDelegate 兼容。
&nbs......
C# 语言规范--1.9 接口(2006-09-03 18:31:00)
摘要:一个接口定义一个协定。实现接口的类或结构必须遵守其协定。接口可以包含方法、属性、索引器和事件作为成员。
示例interface IExample
{
string this[int index] { get; set; }
event EventHandler E;
void F(int value);
string P { get; set; }
}
public delegate void EventHandler(object sender, EventArgs e);
显示了一个包含索引器、事件 E、方法 F 和属性 P 的接口。
接口可以使用多重继承。在下面的示例中,interface IControl
{
void Paint();
}
interface ITextBox: IControl
{
void SetText(string text);
}
interface IListBox: IControl
{
void SetItems(string[] items);
}
interface IComboBox: ITextBox, IListBox {}
接口 IComboBox 同时从 ITextBox 和 IListBox 继承。
类和结构可以实现多个接口。在下面的示例中,interface IDataBound
{
void Bind(Binder b);
}
public class EditBox: Control, IControl, IDataBound
{
public void Paint() {...}
public void Bind(Binder b) {...}
}
类 EditBox 从类 Control 派生,并且同时实现 IControl 和 IDataBound。
在前面的示例中,IControl 接口中的 Paint 方法和 IDataBound 接口中的 Bind 方法是使用 EditBox 类的公共成员实现的。C# 提供了另一种方式来实现这些方法,使得实现类避免将这些成员设置成公共的。这就是:接口成员可以用限定名来实现。例如,在 EditBox 类中将 Paint 方法命名为 ......
C# 语言规范--1.8 结构(2006-09-03 18:30:00)
摘要:类与结构有很多相似之处:结构可以实现接口,并且可以具有与类相同的成员类型。然而,结构在几个重要方面不同于类:结构为值类型而不是引用类型,并且结构不支持继承。结构的值存储在“在堆栈上”或“内联”。细心的程序员有时可以通过聪明地使用结构来增强性能。
例如,将 Point 定义为结构而不是类在运行时可以节省很多内存空间。下面的程序创建并初始化一个 100 点的数组。对于作为类实现的 Point,出现了 101 个实例对象,因为数组需要一个,它的 100 个元素每个都需要一个。class Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Test
{
static void Main() {
Point[] points = new Point[100];
for (int i = 0; i < 100; i++)
points[i] = new Point(i, i*i);
}
}
如果将 Point 改为作为结构实现,如struct Point
{
public int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
则只出现一个实例对象(用于数组的对象)。Point 实例在数组中内联分配。此优化可能会被误用。使用结构而不是类还会使应用程序运行得更慢或占用更多的内存,因为将结构实例作为值参数传递会导致创建结构的副本。
......
C# 语言规范--1.7 类(2006-09-03 18:30:00)
摘要:类声明定义新的引用类型。一个类可以从另一个类继承,并且可以实现多个接口。
类成员可以包括:常数、字段、方法、属性、事件、索引器、运算符、实例构造函数、析构函数、静态构造函数和嵌套类型声明。每个成员都有关联的可访问性,它控制能够访问该成员的程序文本区域。有五种可能的可访问性形式。下表概述了这些形式。
形式
直观含义
public
不限制访问。
protected
访问限于该成员所属的类或从该类派生来的类型。
internal
访问限于此程序。
protected internal
访问限于此程序或从该成员所属的类派生的类型。
private
访问限于该成员所属的类型。
示例using System;
class MyClass
{
public MyClass() {
Console.WriteLine("Instance constructor");
}
public MyClass(int value) {
MyField = value;
Console.WriteLine("Instance constructor");
}
~MyClass() {
Console.WriteLine("Destructor");
}
public const int MyConst = 12;
public int MyField = 34;
public void MyMethod(){
Console.WriteLine("MyClass.MyMethod");
}
public int MyProperty {
get {
return MyField;
}
set {
MyField = value;
}
}
public int this[int index] {
get {
return 0;
}
set {
Console.WriteLine("t......
C# 语言规范--1.6 语句(2006-09-03 18:30:00)
摘要: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.14f;
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:
......
C# 语言规范--1.5 表达式(2006-09-03 18:29:00)
摘要:C# 包含一元运算符、二元运算符和一个三元运算符。下表概述了这些运算符,并将它们按优先级以从高到低的顺序列出:
章节
类别
运算符
第 7.5 节
基本
x.y f(x) a[x] x++ x-- new
typeof checked unchecked
第 7.6 节
一元
+ - ! ~ ++x --x (T)x
第 7.7 节
乘法
* / %
第 7.7 节
加法
+ -
第 7.8 节
移位
<< >>
第 7.9 节
关系和类型检测
< > <= >= is as
第 7.9 节
相等
== !=
第 7.10 节
逻辑 AND
&
第 7.10 节
逻辑 XOR
^
第 7.10 节
逻辑 OR
|
第 7.11 节
条件 AND
&&
第 7.11 节
条件 OR
||
第 7.12 节
条件
?:
第 7.13 节
赋值
= *= /= %= += -= <<= >>= &= ^= |=
当表达式包含多个运算符时,运算符的优先级控制各运算符的计算顺序。例如,表达式 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。
......
C# 语言规范--1.4 自动内存管理(2006-09-03 18:28:00)
摘要:手动内存管理要求开发人员管理内存块的分配和回收。手动内存管理可能既耗时又麻烦。在 C# 中提供了自动内存管理,使开发人员从这个繁重的任务中解脱出来。在绝大多数情况下,自动内存管理可以提高代码质量和开发人员的工作效率,并且不会对表达能力或性能造成负面影响。
示例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);
}
private 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;
}
}
}
显示了一个 Stack 类,它实际上是 Node 实例的一个链接表。Node 实例是在 Push 方法中创建的,当不再需要它们时会对其进行垃圾回收。当任何代码都不再可能访问某个 Node 实例时,该实例就成为垃圾回收的对象。例如,当从 Stack 中移除某项时,相关的 Node 实例就符合了垃圾回收条件,等......
C# 语言规范--1.3 变量和参数(2006-09-03 18:28:00)
摘要:变量表示存储位置。每个变量都属于一种类型,它确定什么样的值可以存储在该变量中。局部变量是在方法、属性或索引器中声明的变量。局部变量是通过指定类型名称和声明符(它指定变量名和可选的初始值)定义的,如: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; // error, a not yet assigned
...
}
}
导致编译时错误,因为它试图在给变量 a 赋值之前使用它。
字段是与类或结构或与类或结构的实例关联的变量。用 static 修饰符声明的字段定义静态变量,不用此修饰符声明的字段则定义实例变量。静态字段与类型关联,而实例变量与实例关联。示例using Personnel.Data;
class Employee
{
private static DataSet ds;
public string Name;
public decimal Salary;
...
}
显示了具有一个私有静态变量和两个公共实例变量的 Employee 类。
形参声明也定义变量。有四种类型的参数:值参数、引用参数、输出参数和参数数组。
值参用于“in”参数传递。在此过程中,自变量的值被传入方法中。因为一个值参在方法中用自己的变量存储(而非原自变量),所以对此参数的修改不会影响到原自变量。值参的变量是通过复制原自变量的值来初始化的。示例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.Writ......