1 绪论
c# 是一种简练,时髦(?),面向对象(object oriented),类型可靠(type-safe)的
编程语言。它(发音:C sharp)是从c/c++发展而来的(?俺觉得更象是java),和c/c++
是一个语系。所以,很容易被c/c++的程序员接受。c#的目标是结合Visual Basic的高产和
C++质朴的力量。
c#将会是vs7的一分子。vs7还支持vb,vc和标记语言——VBScript和JScript。所有这些语言
都会在Next Generation Windows Services (NWGS) platform 中得到支持(c#就需要一个
NWGS SDK包,可以在m$的网站上下载)。有了这个东东(NWGS),c#就不需要自己的类库,
而使用vc或vb这样一些成熟的库。c#也确实没有自己的类库。
废话完了。
1。1 一个老土的例子(就不能换换吗?)*/
/* idontlikeHelloworld.cs : such a out sample :( */
1: using System;
2: class idontlikeHelloworld
3: {
4: static void Main() {
5: Console.WriteLine("i dont like Hello world");
6: Console.ReadLine();
7: }
8: }
/* 如果俺要出书的话,会考虑换个好点的例子。 ^&^
先说说怎样运行。首先,你需要windows2000!(是的,就是它,请各位不要随地丢果皮——
整个香蕉丢给俺就可以了。)然后,需要NWGS SDK!(82.4mb,不算很大噢。嘿嘿,好在
它没有自己的类库。)安装后,在你的程序所在的目录下键入:
csc idontlikeHelloworld.cs (加上一个回车键)
是不是有点复古的味道?这个操作会在和你的*.cs相同目录下产生一个
idontlikeHelloworld.exe文件。双击它,距可以看见:
i dont like Hello world
回车就可以结束它,非常简单。不过,也可以这样:把它存成后缀为.c的文件更好
(即:idontlikeHelloworld.c)。这样就可以用vc的IDE进行打字,编辑。vc的
txt editor是最棒的噢(又要vc,NO!!!)。然后:
csc idontlikeHelloworld.c (加上一个回车键)
最终效果是完全一样的。好,现在分析语法:(c#在语法上完全没有新意 :-| )
1: using System;
using 其实是c++的关键字,在c#中的含义也相仿(就是说俺还不敢100%肯定,抱歉)。using
用在另一个关键字namespace之后。还是先看看namespace。
语法(syntax):(from MSDN)
namespace [identifier] { namespace-body }
俺的理解:
identifier:在这里就是System(请记住:c#和c/c++一样,是区分大小写的!)。System
必须在使用它的范围内是唯一的。即,不能够有第二个System,但可以有system。
而“它的范围”,俺不想详细解说,只有在实践中才可能掌握。而且,初学者根本
不必知道!俺也是近来才知道还有个namespace和using。 :)
在{ namespace-body }中的是真正有用的东东,包括第五行的“Console.WriteLine”的声明和
定义(后面还会提到)。System是由NWGS定义的,咱们只需用(using)它即可。至于System在
什么文件里定义,咱就不用管了!交给编译器(就是刚才那个“csc.exe”)去寻找。这就代替
了c/c++中的“#i nclude”,可以说是近了一步,避免大量烦人的细节。如果你没学过c/c++,
就不用理会。namespace 在后面还会谈到。
2: class idontlikeHelloworld
class:是c语系中另一个关键字“类”。表示一系列的特性(官方说法:属性)和行为方法,有
了它你的程序就可以“另类”,创造与别不同的有你特色的东东噢!在这里,俺就定义了
“idontlikeHelloworld”。注意:这也是c#强制的,对于每一个可执行的程序都必须有。你想干
的事就可以记录在紧跟着你定义的class后面的一对花括号。注意:“{”和“}”一一对应的,
“(”和“)”同样。
4: static void Main() {
Main()是本例子第一个动作(行为方法),干的第一件事。它是属于俺定义的idontlikeHelloworld
类的方法。并且是c#强制的,是程序的真正开始!在紧跟在它后面的“{}”中的语句顺序,就是程序
的运行顺序!本例中只有一行(第六行干嘛用?你可以去掉再编译一次看看),输出一句话。
5: Console.WriteLine("i dont like Hello world");
非常奇怪,Console(再次提醒:注意大小写)不是俺定义的,从何而来?它其实是属于System
namespace 的一个class。WriteLine()是Console类中的一个方法,用来显示一句话(字符串)。
这里只是用了这个方法的1/18!并且是最简单之一!其他的有机会再说。你也可以用
“Console.WriteLine”在“NGWS SDK Documentaion”中搜索“Console.WriteLine”,记住复选
“仅搜索标题”,它会列出19项。好啦,完了!其实,还有“.”没说呢!呵呵...lei si la!!!!
(续前)
“.”被称为分隔符(separator),用来连接名字,如上面的“Console.WriteLine”,就把类和它的
方法连接。通过这种方式,咱们就可以使用现成方法集合。这里再回顾一下俺的例子,看看namespace和
“.”是如何连用的,还有为什么要使用namespace这个关键字。把例子稍微改一下:*/
/* idontlikeHelloworld.cs */
1: //using System;
2: class idontlikeHelloworld
3: {
4: static void Main() {
5: System.Console.WriteLine("i dont like Hello world");
6: System.Console.ReadLine();
7: }
8: }
/* 看见了,当俺注销掉“using System;”后,在第五行和第六行加了“System”。程序的结果不会改
变。但是,很明显的这样比较罗嗦,所以引入了“namespace”。其实,class应该可以完成同样的功能。
不过,设计者可能不想让一个关键字涵盖太多的功能。记得在c向c++发展的时候,引入了“class”,而
不是扩展“struct”关键字的功能;又比如“=”只用于赋值,“==”只用于判断相等。这是c/c++和c#
在语法上其中一个重要的特点。这样设计的好处很多。有机会再聊噢。
如果你没学过c/c++,以下的内容可以跳过。c#与c/c++在语法上还是有区别的,比如:
1。c#根本没有“::”;“->”只在程序中很小的片断中。在c#中应采用“.”。
2。c#无须先声明定义,再使用。与java相同。
3。c#取消了用“#i nclude”导入其他的程序文本文件,而采用象征性的句柄引入他人的代码。这样一来,
就排除了编程语言间的障碍,方便地使用其它语言编写的库。如“Console”类可以是c#或者是其他任一种语言编写的。
1。2 自动化的内存管理(Automatic memory management)
手动管理内存需要程序员自行分配和释放内存块。这要求程序员有清晰的头脑和对整个运行过程有十分的
把握(好难!)。而c#把程序员从这难以承担的任务中解放出来。在多数的情况下,这种自动内存管理提
高代码的质量和程序员的生产力。并且,不会对程序的意图和执行产生幅面的影响(?俺可不相信m$的鬼
话)。不过,估计比java的回收站好一点吧。因为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);
}
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;
}
}
}
class Test
{
static void Main() {
Stack s = new Stack();
for (int i = 0; i < 10; i++)
s.Push(i);
while (!s.Empty)
Console.WriteLine(s.Pop());
}
}
/*
stack类实现了一系列Node的实例。大家可以看看stack类的Push方法。Node的实例就是在Push方法中创建的。
就是“first = new Node(o, first);”。请记住这个“new”噢。它就是用来创建类实例的。相关的语法太
多,遛到后面用一节详细讲。这里只是要了解自动内存管理(Automatic memory management)好处?!“new”
是负责初始化类实例。而在c/c++中释放这些实例要用另一个关键字“delete”。但是在什么时候用delete呢,
这通常是很费神的活,老手也会阴沟里翻船。何况是俺呢!但在c#中有不用了。例子里就没有用“delete”。
当Node的实例不需要时,垃圾收集器(garbage collector)自动销毁它,不用俺操心喽。这点到和java挺
像的(可能是抄的)。
在一个test类里,俺用了一个循环,对stack类的实例的Push方法赋值十次。于是,Push创建了Node的十个实
例(instance)。然后用Pop把它们显示出来。其顺序正好与创建的顺序相反。
这个例子相当的好,是stack
的一个典型,也很好的表述了自动内存管理的机制。但也不好懂,好在这一节不是写给毫无基础的网友看的。
俺自个都花了几分钟看明白,各位大虾更是没问题。
其实,当显示完了“10”以后,就会有一个Node的实例符合被释放的条件,但垃圾收集器并不一定会这样做。
也就是说,它的行为并不确定(这和java一样,俺猜)。有时候,这种行为会带来一些负面影响。起码是性
能降低。自动内存管理本身也是有问题的。因为它很难管理一些特殊情况。有一些关于java的垃圾收集器的
文章也有提到。m$也不会好得了多少。所以,m$有个不安全代码的术语(unsafe code),用来为高级用户服
务。即,用户可以不采用垃圾收集器。但必须用“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);
}
}
/*
俺对这个例子不是很满意,也让俺有点迷惑,有机会再自己写一个。很简单,只是可以用指针了!万岁!
其实,俺对这一节最没有把握了!有不少地方都不能自圆其说!所以,请各位大虾大力批评。*/
1。3 类型
c#支持两种基本的类型:一种是值(value types),一种是引用(reference types)。值包括简单类型
(char、int、和float),枚举(enum)和结构(struct)。引用包括类(class),界面(interface),
代表(delegate)和数组阵列(array)。值与引用不同之处在于:值直接存储它的数据内容;而引用存储对象
的引用。是不是粉费解?!打个比方吧。你在某地买了套别墅(好棒噢)。却从未去过,只知道地址,怎
么办?你可以坐出租车,司机看了地址就知道怎样走不用你操心。你手里的地址就好像对象的名字,你把
它写在程序中,就好像把地址给了司机。司机就是你的编译器,它知道该去哪。你豪华的房子就好比那个
NGWS SDK开发包(82mb噢,够豪华了!俺的m啊--就这样烧喽)。房子里有你想要的东东,比如你想写一句
话(i dont like Hello world),就好像上面例子,要用到“WriteLine”。于是,你就给出“WriteLine”
的地址,比如:“Console.WriteLine”。明白?!俺可累了。zzz... (强打精神)不知道你想到没有,
值和引用的区别可以引出一个重要特性。值的变量和变量存储的数据是一一对应的,唯一性。而引用则不
然。引用中不同的变量可以引用同一个对象的实例。当其中一个变量改变实例的值时,其他引用这个实例的
变量也会受到影响(当然,变量本身并没有改变,即,地址没变)。瞧,变量只是说明存储对象的位置(地
址),而不是对象本身。就好像你漂亮的房子被烧了,但你的地址并没有改变,但地址对应的房子就没了。
也许是别人也有这个地址,他去烧了你的房子!好了,在给个例子:*/
1: using System;
2: class CValue
3: {
4: public int Value = 0;
5: }
6: class Test
7: {
8: static void Main() {
9: int val1 = 0;
10: int val2 = val1;
11: val2 = 123;
12: CValue ref1 = new CValue();
13: CValue ref2 = ref1;
14: ref2.Value = 123;
15: Console.WriteLine("Values: {0}, {1}", val1, val2);
16: Console.WriteLine("Refs: {0}, {1}", ref1.Value, ref2.Value);
17: }
18: }
/* 下面是输出的结果:
Values: 0, 123
Refs: 123, 123
啊哈,应该粉清楚了吧。变量val1和变量val2互不影响,它们各自有自己的存储空间。而ref2复制
了ref1,所以,它们引用了同一个对象的实例。当改变它们其中一个的时候,就会影响到另一个的
值。
1。5 数组类型(Array types)
数组可以是一维的,也可是多维的。数祖的成员可以是整齐的,也可以是变长(jagged)的。
一维的数组是最普通,最简单的。这里值给出一个例子,就不多解释了。*/
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]);
}
}
/* 结果如下:
arr[0] = 0
arr[1] = 1
arr[2] = 4
arr[3] = 9
arr[4] = 16
我们还可以比较的看看多维,规则,变长的数组的定义和赋值:*/
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=6000。相反,变长
数组就有点不同,它的每一个维度都必须单独定义。如j2的第一维度是3,第二个是6,第
三个是9,所以总长度是:1*3+1*6+1*9=18。
上面对数组的赋值是严谨的风格,在某种情况下,我们可以简化写法,但我总觉得这种简化
应用限制太多,容易出错。在这里就不作介绍了。这里再给一个例子说明函数中的参数如何
赋值*/
class Test
{
static void F(long[] arr) {}
static void Main() {
F(new longt[] {1, 2, 3});
}
}
赶出一编!请指正!
1。6 统一系统类型(Type system unification)
c#独创了一种类型——统一系统类型(为了这个累刑,我头疼死了。谁有更好的名字,请务必告诉
我)。总之,所有的其他类型,包括值和引用,都可以被当作统一系统类型来对待。从概念上说,
所有的类型都从它派生。这样,其他的类型就可以使用统一系统类型的属性和方法。包括一些“简
单”类型,如:int。还是给个例子吧:*/
using System;
class Test
{
static void Main() {
Console.WriteLine(3.ToString());
}
}
/*“3.ToString()”调用了object的“ToString()”方法。相信学过c/c++的朋友都知道要输出一个
数字有多麻烦,现在就省事了。再看一个:*/
class Test
{
static void Main() {
int i = 123;
object o = i; // boxing
int j = (int) o; // unboxing
}
}
/* 这个像帽子戏法的例子中,从“int”转换成“object”,又转换回来。这样一来,在值和引用
之间就架起了一座桥梁。这样有什么用呢。即兴举一个常见的例子...就min把。在c/c++中:*/
// c/c++ code
void min(int i, int j)
{
return ((i < j) ? i : j);
}
/* 如果比较的不是int,或者说可能是int,也可能是float、double呢?可以这样:*/
template<class T>
T min (T i, T j)
{
return ((i < j) ? i : j)
}
/* 用c#可以:*/
void swap (object a, object b)
{
return ((i < j) ? i : j);
}
/* 我想大家一定看出来第二个例子要比较一个int和一个float的话,还需要一些转换,而第三个
例子就可以比较所有的变量!这个灵活度简直太大了。所以,我私以为,大家使用时一定要小心!
它在比较一个int和一个class的时候决不会报错的。呵呵,我发现我的翻译总是越跑越远,总是
扣不住原文。篡改甚多,敬请原谅!
1。7 语句(Statements)
c#借用了c/c++大多数的语句方法,不过仍然有些值得注意的地方。还有些地方是有所改动的。
在这里,我只提一些c#特有的东东。
1。7。10 “foreach”语句
“foreach”语句列举一个集合内的所有元素,并对这些元素执行一系列的操作。还是看看例子吧:*/
using System;
using System.Collections;
class Test
{
static void WriteList(ArrayList list) {
foreach (object o in list)
{
int i = (int) o;//如果是for语句,这里一定会报错!
Console.WriteLine(0);
Console.WriteLine(++i);
}
}
static void Main() {
ArrayList list = new ArrayList();
for (int i = 0; i < 10; i++)
list.Add(i);
WriteList(list);
}
}
/*这个例子用“foreach”扫描了整个“list”,并把“list”中所有的元素打印出来。有时候还是
挺方便的。
1。7。15 安全检查开关(The checked and unchecked statements)
“checked”和“unchecked”语句用来控制数学运算和完整类型转换的检查工作。“checked”检查它
作用的域中可能出现的违例,并抛出一个异常;而“unchecked”则阻止所有的检查。举个例子:*/
using System;
class Test
{
static int x = 1000000;
static int y = 1000000;
static int F() {
checked {return (x * y);} // 抛出 OverflowException
}
static int G() {
unchecked {return (x * y);} // 返回 -727379968
}
static int H() {
return x * y; // 缺省状态。
}
static void Main() {
F(); //可以注销掉此行试试。
Console.WriteLine(G());
Console.WriteLine(H());
}
}
/*
在编译过程中不会有任何错误出现。因为“checked”和“unchecked”只在运行时才起作用。值得一说的是
H()。它的缺省状态和编译器当前的缺省溢出检查的状态有关。但返回的结果肯定和F()或G()中的任一个相同。
再看一个例子:*/
using System;
class Test
{
const int x = 1000000;
const int y = 1000000;
static int F() {
checked {return (x * y);} // 编译器警告(Compile warning):溢出(overflow)
}
static int G() {
unchecked {return (x * y);} // 返回 -727379968
}
static int H() {
return x * y; // 编译器警告(Compile warning):溢出(overflow)
}
static void Main() {
Console.WriteLine(F()); //可以注销掉此行试试。
Console.WriteLine(G());
Console.WriteLine(H()); //可以注销掉此行试试。
}
}
/* 当F()和H()求值的时候,就会引起一个编译警告。而在G()中,因为有了“unchecked”,屏蔽了这个警
告。要注意的是“checked”和“unchecked”都不能对函数的返回值进行操作!比如:*/
class Test
{
static int Multiply(int x, int y) {
return x * y;
}
static int F() {
checked{ return Multiply(1000000, 1000000); } // 与 return Multiply(1000000, 1000000);
} // 有相同的效果。
}
/* 其实大家稍微想一下知道为什么m$没有这么做!对这个内容的讨论超出本文的范围和俺的能力之外哦。
在c#中,所有的十六进制数都是uint。如果用强制类型转换会引起编译器报错。用“unchecked”则可以
跳过这个机制,把uint的十六进制数转化为int。如:*/
class Test
{
public const int AllBits = unchecked((int)0xFFFFFFFF);
public const int HighBit = unchecked((int)0x80000000);
}
/* 上例所有的常数都是uint,而且超过了int的范围,没有“unchecked”,这种转换会引发一个编译器错
误。注意:上面用的是“unchecked”操作符。不是语句。不过它们之间除了一个用“()”,另一个用
“{}”以外,几乎一样。BTW,“checked”同样。
1。7。16 “lock”语句(The lock statement)
“lock”获得一个相互排斥的对象锁定。(俺查过一些资料,但都没有清晰说明,暂不介绍)
文章来源:
http://www.blogjava.net/kuxiaoku/articles/94806.html
posted on 2007-01-19 00:14
苦笑枯 阅读(306)
评论(0) 编辑 收藏 所属分类:
C#