《电脑报》读者论坛 » 编程魔方 » C#进阶[转贴]

页: [1]

小恐龙 2006-3-9 00:30
C#进阶[转贴]

-------------------------------------------以下内容为转贴---------------------------------------------
SUNWEN教程之----C#进阶
(一)
mrfat@china.com

写在前面的话

在写这些文章之前,我对C#一无所知.只从前辈那里听说了一些,说他很像JAVA.因为我这段时间一直在看JAVA,结果什么明堂没看出来(只能怪我太笨:),其实我想还是没有实践的机会),所以就很想换换口味.其实很早就听说了C#,想看一看这个东东,只是上M$的网站一看,.NET的东东要一百多兆呢!看了就怕,何况全部下载下来,要费我一整张电话卡呢?幸好我一个朋友先把.NET下载下来了,我拿了硬盘就跑过去拷过来了.一个SETUP.EXE,足足有106M之巨.所以,我在此要特别感谢这位朋友阿KEN,没有他的帮助,就没有下面的文章.

写这篇文章的时候,我就有些害怕,怕自己水平不够,错误太多,挨别人骂.在写的过程中,发现还是比较顺利的,因为这些跟原先的JAVA有很多相象的地方.而JAVA,很多前辈都有很深的研究.不过,由于本人水平有限,错误之处在所难免,如果大家发现有什么错误之处,还望指正,在下感激不尽!我的MAIL:mrfat@china.com.小弟先在此谢过了!

以下的文章适用于有一些JAVA/C++编程经验的人,如果有JAVA的编程经验,那就更好了,因为我在里面很多地方把C#与JAVA对比.如果你没有任何的编程序经验,可以到OICQ的主页的社区去找另一系列的C#教程,那个可能更适合你.

还有就是由于以下文章是在闲的时间写的,废话比较多,大家看了不要吐血哟@#$%^&*!好了,下面开始吧!

[code]000: // CommandLine\cmdline1.cs
001: using System;
002:
003: public class CommandLine
004: {
005:     public static void Main(string[] args)
006:     {
007:         Console.WriteLine("Number of command line parameters = {0}", args.Length);
008:         for(int i = 0; i < args.Length; i++)
009:         {
010:             Console.WriteLine("Arg[{0}] = [{1}]", i, args[i]);
011:         }
012:     }
013: }[/code]

如果你的输入为:

cmdline1 A B C

那么它的输出为:
Number of command line parameters = 3
Arg[0] = [A]
Arg[1] = [B]
Arg[2] = [C]

哈哈,看出了其中的秘密了吧!是的{0}是一个标记,它告诉系统,这里留下了给第0个参数用,在程序中,这第0个参数就是i.因此,打印出来的就是Arg[0],面不是Arg[{0}]了,哈哈!

例二向大家展示了foreach这个语句的用法,其实在ASP中,这个经常用到:


[code]000: // CommandLine\cmdline2.cs
001: using System;
002:
003: public class CommandLine2
004: {
005:     public static void Main(string[] args)
006:     {
007:         Console.WriteLine("Number of command line parameters = {0}", args.Length);
008:         foreach(string s in args)
009:         {
010:             Console.WriteLine(s);
011:         }
012:     }
013: }[/code]

很显然,args是一个数组,而且是字符型的.foreach的意思就是把其中的所有元素都循环完.运行得出:

>cmdline2 John Paul Mary
Number of command line parameters = 3
John
Paul
Mary
好了,同志们,休息一下,我要睡觉了!88,下次再见!

终于又到一个白天了,起床,伸个懒腰,又坐到了电脑前面.今天我要向大家讲的是C#的数组(Arrays).C#中的数组和其它一些优秀的语言一样,也是从0开始计的,这从我们以前的例子里可以看出来,也就是说,一个数组的第一个元素是a[0],而不是像VB的a(1).虽然是这样,但是你还是要注意一些区别.

在声明一个数组的时候,方括号必须跟在类型后面,而不能跟在变量名后面,如:
int[] table; //不能写成int table[]
这一点显然与JAVA是不同的,在JAVA中这样是可以的.

还有就是在C#中你可以不指定数组的大小,这与C语言是不一样的.这使得你可以指定任意长度的数组,如下:
int[] numbers; // 它的长度是任意的
当然,你也可以指定它的大小:
int[10] numbers;//指定了一个长度为10的数组.

在C#中,支持的数组包括:单维数组,多维数组和多重数组.它们的声明方法如下:
单维数组:
int[] numbers;
多维数组:
string[,] names;
多重数组:
byte[][] scores;


声明一个数组并不代表已经建立了它.在C#中,所有的数组元素都是对象(倒!怎么跟JAVA说得一样&*%$#@),所以在建立它之前,首先要将它实例化:
单维数组:
int[] numbers = new int[5];
多维数组:
string[,] names = new string[5,4];
多重数组:


[code]byte[][] scores = new byte[5][];
for (int x = 0; x < scores.Length; x++)
{
   scores[x] = new byte[4];
}[/code]


呵呵,这有点奇怪吧,先不用理它,以后再说.

我们同样可以建立更大的数组,比如一个三维数组:
[code]int[,,] buttons = new int[4,5,3]; [/code]


我们甚至可以混合多维数组和多重数组,下面的例子说明了这些:
int[][,,][,] numbers;

下面的例子展示了以上所有构建数组的方法:


[code]000: // Arrays\arrays.cs
001: using System;
002: class DeclareArraysSample
003: {
004:     public static void Main()
005:     {
006:         // Single-dimensional array
007:         int[] numbers = new int[5];
008:
009:         // Multidimensional array
010:         string[,] names = new string[5,4];
011:
012:         // Array-of-arrays (jagged array)
013:         byte[][] scores = new byte[5][];
014:
015:         // Create the jagged array
016:         for (int i = 0; i < scores.Length; i++)
017:         {
018:             scores[i] = new byte[i+3];
019:         }
020:
021:         // Print length of each row
022:         for (int i = 0; i < scores.Length; i++)
023:         {
024:             Console.WriteLine("Length of row {0} is {1}", i, scores[i].Length);
025:         }
026:     }
027: }[/code]

它的输出是:

Length of row 0 is 3
Length of row 1 is 4
Length of row 2 is 5
Length of row 3 is 6
Length of row 4 is 7
在C#中数组的初始化可以在建立时就初始化,和JAVA和C一样,用的是{}.当然,很明显,你的初始化值必须与你声明的数组类型一样,比如你定义了一个int类型的,你就不能给它一个String,唉,JAVA看多了,在C#中,String应写为string,要不然,又要出错了.SUNWEN可能在后面的课程中出现这样的错误,还望大家指正.呵呵!

下面的例子说明了数组的初始化:

[code]int[] numbers = new int[5] {1, 2, 3, 4, 5};
string[] names = new string[3] {"Matt", "Joanne", "Robert"}; [/code]


你也可以省略数组的大小,如:

[code]int[] numbers = new int[] {1, 2, 3, 4, 5};
string[] names = new string[] {"Matt", "Joanne", "Robert"};[/code]

你甚至可以省略new语名,如果你给了值:


[code]int[] numbers = {1, 2, 3, 4, 5};
string[] names = {"Matt", "Joanne", "Robert"};[/code]

在C#中,数组的访问和C/C++/JAVA是一样的,下面的语句建立了一个数组,并将它的第五个元素赋值为5:
int[] numbers = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
numbers[4] = 5;

如果你没有C/JAVA/C++的编程经验,那么SUNWEN在此提醒,numbers[4]表示的是这个数组的第五个元素,因为我在前面已经说过了,数组是从0开始计的,所以0,1,2,3,4正好是第五个,所以....(台下:笨蛋,你以为我们不知道呀,快继续说!)

下面的例子是关于多维数组的:

[code]int[,] numbers = { {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10} };
numbers[1, 1] = 5; [/code]

再次注意,C#中的所有数组都是对象(faint,D版),所以,你可以用访问对象的方法,来访问数组.而System.Array就是数组的抽象.你可以参看文档来看Array类支持的方法.举个例子来说吧,你可以用length属性来访问数组的长度.如下例:

[code]int[] numbers = {1, 2, 3, 4, 5};
int LengthOfNumbers = numbers.Length;[/code]
噢,好了,又完了一课,现在是北京时间上午9点16分,我要休息一下了!哈哈!待会见!

小恐龙 2006-3-9 00:31
这次我要向大家讲的是C#中的属性.属性是什么呢,比如说我是男的,男就是我的一人属性.我是一个大一的学生,大一也是我的一个属性.属性就是一个对象的性质.很简单吧,呵呵!下面,我给出两个例子,第一个例子出明了如何声明一个可以修改的属性,另一个例了建立了一个抽象的属性(Abstract),并且说明了如何在子类中丢弃它.好,下面让我们开始吧.

例子一:


[code]000: // Properties\person.cs
001: using System;
002: class Person
003: {
004:     private string myName ="N/A";
005:     private int myAge = 0;
006:
007:     // 声明一个字符型的属性Name
008:     public string Name
009:     {
010:         get
011:         {
012:            return myName;
013:         }
014:         set
015:         {
016:            myName = value;
017:         }
018:     }
019:
020:     // 声明一个int型的Age属性
021:     public int Age
022:     {
023:         get
024:         {
025:            return myAge;
026:         }
027:         set
028:         {
029:            myAge = value;
030:         }
031:     }
032:
033:     public override string ToString()
034:     {
035:         return "Name = " + Name + ", Age = " + Age;
036:     }
037:
038:     public static void Main()
039:     {
040:         Console.WriteLine("Simple Properties");
041:
042:         // 建立一个Person的实例
043:         Person person = new Person();
044:
045:         //打印出它的属性
046:         Console.WriteLine("Person details - {0}", person);
047:
048:         // 对属性进行一些设置
049:         person.Name = "Joe";
050:         person.Age = 99;
051:         Console.WriteLine("Person details - {0}", person);
052:
053:         // 增加年龄
054:         person.Age += 1;
055:         Console.WriteLine("Person details - {0}", person);
056:     }
057: }[/code]

这个例子的输出是:

Simple Properties
Person details - Name = N/A, Age = 0
Person details - Name = Joe, Age = 99
Person details - Name = Joe, Age = 100

好了,又起床了,昨天写到这,就跑到床上睡了,呵呵.现在是五一的第二天,看看我今天能写几篇,昨天写了二篇.

从上面的程序我们可以看出,一个类的属性的设置,借用了VB的概念,和JAVA中不尽相同了.(这就是M$,**!)有的朋友可能很奇怪,为什么我们可以用Console.WriteLine()来打印一个对象person呢.其实道理很简单,和在JAVA中的一样,当调节器用一个打印方法时,这个对象自动调用它的ToString()(在JAVA中是toString,**,又差点犯错!)方法.在33行,我们可以看到有这个方法的影子,override这个关键字大概就是覆盖父类的方法吧,这是不是有点多余?我们可以看到,一个对象的属性的设置是通过一个get()和set()组合来完成的,当然,还有一个value这个东东.补充一下,你还可以控制一个属性的读/写权限,只是把get()和set()简单地去掉就可以了,比如你不要写的属性,就不要set()就可以了,如果你不要读,就不要get()吧.总得感觉,C#在这点上没有JAVA来的灵活(完了,又要被盖痴打了!).

第二个例子:

这个例子说明了如何建立抽象的属性(Abstract),什么是抽象的属性呢,所谓的抽象的属性呢,就是....(唉,每次废话都这么多!FT) 一个抽象类并不提供执行属性存取程序,并且,他可以在子类中被忽略.下面的例子有三个文件,你要分别编译它们才能得到结果,它们是:

abstractshape.cs: Shape类,包含一个Area抽象属性
shapes.cs: Shape的子类
shapetest.cs: 显示程序.
要编译这些程序,运行:csc abstractshape.cs shapes.cs shapetest.cs就可以了.运行完之后,它会产生shapetest.exe这个可执行程序.

[code]000: // Properties\abstractshape.cs
001: using System;
002:
003: public abstract class Shape
004: {
005:    private string myId;
006:
007:    public Shape(string s)
008:    {
009:       Id = s;   // 这句调用了Id属性的set构建器
010:    }
011:
012:    public string Id
013:    {
014:       get
015:       {
016:          return myId;
017:       }
018:
019:       set
020:       {
021:          myId = value;
022:       }
023:    }
024:
025:    public abstract double Area
026:    {
027:       get;
028:    }
029:
030:    public override string ToString()
031:    {
032:       return Id + " Area = " + double.Format(Area, "F");
033:    }
034: }[/code]

看这个程序, 实际上非常地简单,当这个类对的对象被建立时,初始化部分为007-010,它把建立对象的参数s给了Id这个属性.然后进行了上一个例子的操作.实际上,我们可以把抽象属性与JAVA中的接口(Interface)相比,它们只提拱一个方法的名称,而不提供这个方法的内容.就像Area这个抽象属性,有一个get,但是它并没有指定get方法(也许它不能称之为方法)里的内容,即要get做什么事情. 这个事情,由它的子类来做.

第二个文件:在这个文件中,一个类覆盖了(override)了Area属性.

[code]000: // Properties\shapes.cs
001: public class Square : Shape
002: {
003:    private int mySide;
004:
005:    public Square(int side, string id) : base(id)
006:    {
007:       mySide = side;
008:    }
009:
010:    public override double Area
011:    {
012:       get
013:       {
014:          return mySide * mySide;
015:       }
016:    }
017: }
018:
019: public class Circle : Shape
020: {
021:    private int myRadius;
022:
023:    public Circle(int radius, string id) : base(id)
024:    {
025:       myRadius = radius;
026:    }
027:
028:    public override double Area
029:    {
030:       get
031:       {
032:          return myRadius * myRadius * System.Math.PI;
033:       }
034:    }
035: }
036:
037: public class Rectangle : Shape
038: {
039:    private int myWidth;
040:    private int myHeight;
041:
042:    public Rectangle(int width, int height, string id) : base(id)
043:    {
044:       myWidth  = width;
045:       myHeight = height;
046:    }
047:
048:    public override double Area
049:    {
050:       get
051:       {
052:          return myWidth * myHeight;
053:       }
054:    }
055: }[/code]

这个例子就有些让我们一头雾水了,:是干什么的,好象是继承,相当于JAVA中的extends吧.我想是的.我们先看一看吧.

下面的第三个文件就是一个测试文件了,很简单,大家看一看.

[code]000: // Properties\shapetest.cs
001: public class TestClass
002: {
003:    public static void Main()
004:    {
005:       Shape[] shapes =
006:          {
007:             new Square(5, "Square #1"),
008:             new Circle(3, "Circle #1"),
009:             new Rectangle( 4, 5, "Rectangle #1")
010:          };
011:      
012:       System.Console.WriteLine("Shapes Collection");
013:       foreach(Shape s in shapes)
014:       {
015:          System.Console.WriteLine(s);
016:       }
017:         
018:    }
019: }[/code]

从这个例子上看,:这个符号的确是extends的意思,就是继承.继承是什么意思,说白了,就是生孩子的意思.比如下面一句sunwenson extends sunwen,这名就是说sunwenson这个类继承了sunwen,sunwenson这个类有sunwen类的所有东西,同时可以添加和删除sunwen中的一些东西.就这么简单,但是这是现代软件发展的一项重要技术,因为它可以使软件的重用性大大提高.唉,这些就只有大三大四的人说了,我就没资格了.呵呵.

这个程序的输出是:

Shapes Collection
Square #1 Area = 25.00
Circle #1 Area = 28.27
Rectangle #1 Area = 20.00
完了,这一节又完了.要理解这一节,有一定的难度, 特别对那些没有JAVA或C++编程经验的朋友.不过不要害怕,鼓起勇气学下去,一定会有所收获的. 我也要休息一下了,嘿嘿,早饭还没有吃呢!

小恐龙 2006-3-9 00:33
我现在要说的是库(libraries),和大家一起学习如何用C#建立一个DLL文件.说起DLL,肯定是无人不知,无人不晓,这个WINDOWS的典型代表,同时也经常是大家功击的对象.呵呵,不管怎么样,学还是要学的.我们下面就开始,如何用命令行方式将一个C#程序编译成DLL,和如何在客户端使用他.

这个例子包括两个文件,一个是Factorial.cs,作用是计算一个数字的阶乘.还有一个是DigitCounter.cs,作用是计算传过来的字符串参数中的数字的数目.

我们可以这样来建立库,在命令行方式下这样做:
[code]csc /target:library /out:Functions.dll Factorial.cs DigitCounter.cs[/code]

下面讲一下各个参数的用法:

[code]/target:library:向系统指出输出的是一个DLL库,而不是一个EXE的可执行文件.
/out:Functions.dll:指定输出的DLL的文件名,即Functions.dll,一般地,如果你省略了第一个参数,那么默认的文件名将是第一个文件的文件名,即Factorial.dll.[/code]

下面我们再来建立一个文件,即使用这个库的文件,叫客户端文件,FunctionClient.cs.建立好后,用下面的语名编译:

[code]csc /out:FunctionTest.exe /R:Functions.DLL FunctionClient.cs [/code]

下面说一下这个编译语句的用法:

[code]/out:FunctionTest.exe:指出输出的文件名是FunctionTest.exe
/R:Functions.DLL:指出要引用的库,如果不是在当前目录下,必须要指出其的完整路径.[/code]

下面我就把这几个文件的代码写在下面:

[code]000: // Libraries\Factorial.cs
001: using System;
002:
003: namespace Functions
004: {
005:     public class Factorial
006:     {
007:         public static int Calc(int i)
008:         {
009:             return((i <= 1) ? 1 : (i * Calc(i-1)));
010:         }
011:     }
012: }[/code]
这是Factorial.cs这个文件的代码.在003行中,namespace的意思是名字空间,据M$的介绍,库必须根据它的名字空间打包,以使.NET能够正确地载入你的类.

[code]下面是DigitCounter.cs这个文件的内容:

000: // Libraries\DigitCounter.cs
001: using System;
002:
003: namespace Functions
004: {
005:     public class DigitCount
006:     {
007:         public static int NumberOfDigits(string theString)
008:         {
009:             int count = 0;
010:             for ( int i = 0; i < theString.Length; i++ )
011:             {
012:                 if ( Char.IsDigit(theString[i]) )
013:                 {
014:                       count++;
015:                 }
016:             }
017:
018:             return count;
019:         }
020:     }
021: }[/code]
注意,这个例子中的namespace应与第一个的一致,因为它们是同一个库中的.NumberOfDigits方法计算了参数中的数字的个数.

第三个文件是FunctionClient.cs


我们知道,一个库一旦建立,就可以被别的类利用(废话,要不然怎么叫库呢?).下面的C#程序就利用了我们刚才建立的库中的类.

[code]000: // Libraries\FunctionClient.cs
001: using System;
002: using Functions;
003: class FunctionClient
004: {
005:     public static void Main(string[] args)
006:     {
007:         Console.WriteLine("Function Client");
008:
009:         if ( args.Length == 0 )
010:         {
011:             Console.WriteLine("Usage: FunctionTest ... ");
012:             return;
013:         }
014:
015:         for ( int i = 0; i < args.Length; i++ )
016:         {
017:             int num = Int32.Parse(args[i]);
018:             Console.WriteLine(
019:                "The Digit Count for String [{0}] is [{1}]",
020:                args[i],
021:                DigitCount.NumberOfDigits(args[i]));
022:             Console.WriteLine(
023:                "The Factorial for [{0}] is [{1}]",
024:                 num,   
025:                 Factorial.Calc(num) );
026:         }
027:     }
028: }[/code]
在002行中,一个using Functions指明了引用Functions.DLL这个类.

如果我们在命令行中键入如下命令,就可以看到输出:


FunctionTest 3 5 10

输出:

Function Client

The Digit Count for String [3] is [1]

The Factorial for [3] is [6]

The Digit Count for String [5] is [1]

The Factorial for [5] is [120]

The Digit Count for String [10] is [2]

The Factorial for [10] is [3628800]

注意:当你运行这个.EXE文件时,它引用的DLL文件可以是在当前目录,子目录,或是CORPATH这个环境变量.CORPATH这个环境变量是在.NET环境中的类路径,用来指引系统寻找类.说白了,就是JAVA中的CLASSPATH,明白了吧,呵呵.

好了,又完了一篇,今天的任务完成了,可以休息了.唉,有什么好玩的呢?

小恐龙 2006-3-9 00:34
现在我要说的是C#中的版本处理.其实这是任何一个软件必须要考虑的问题.每个软件都不只一个版本(除了我写的以外),因此版本处理显得非常地重要.JAVA很好地处理了这个问题,而我个人认为C#借鉴了JAVA的处理方法,所以,也做得很好.

在C#中,如果你在声明一个方法的时候用了virtual这个关键字,那么,在派生类中,你就可以使用override或者new关键字来弃用它或是忽略它.如果你在父类中用了virtual这个关键字,而在其派生类中又没有用override或new关键字,而直接引用一个同名方法的话,编译器将会报错,并将以new方式,即忽略派生类中的方法的方式来运行.下面的例子可以帮助你来理解:


[code]000: // Versioning\versioning.cs
001: public class MyBase
002: {
003:     public virtual string Meth1()
004:     {
005:         return "MyBase-Meth1";
006:     }
007:     public virtual string Meth2()
008:     {
009:         return "MyBase-Meth2";
010:     }
011:     public virtual string Meth3()
012:     {
013:         return "MyBase-Meth3";
014:     }
015: }
016:
017: class MyDerived : MyBase
018: {
019:     public override string Meth1()
020:     {
021:         return "MyDerived-Meth1";
022:     }
023:     public new string Meth2()
024:     {
025:         return "MyDerived-Meth2";
026:     }
027:     public string Meth3() // 系统在这里将会有一个警告,并且将会隐藏方法Meth3()
028:                           
029:                           
030:     {
031:         return "MyDerived-Meth3";
032:     }
033:
034:     public static void Main()
035:     {
036:         MyDerived mD = new MyDerived();
037:         MyBase mB = (MyBase) mD;
038:
039:         System.Console.WriteLine(mB.Meth1());
040:         System.Console.WriteLine(mB.Meth2());
041:         System.Console.WriteLine(mB.Meth3());
042:     }
043: }[/code]

输出:

MyDerived-Meth1
MyBase-Meth2
MyBase-Meth3
可以很明显地看出来,后两个new关键字的输出是父类中的方法的输出,所以可以看出,new这个关键字的作用是如果在以前的版本中有这个方法,就沿用以前的方法,而不用我现在方法内容.而virtual的方法的作用正好相反,它的作用是如果在父类中有这样一个方法,则用我现在写的方法内容,让以前的滚蛋!不过,这里用new好象不太好,让人误解(糟了,盖痴又要打我了!&*%$#@).

如果你把第037行去掉,把039-041中的mB全部改为mD,输出又变为:

MyDerived-Meth1
MyDerived-Meth2
MyDerived-Meth3

这又说明了什么呢,说明了派生类的对象只有在被父类重塑的时候,override和new关键字才会生效.呵呵,这样说的确有点难以理解,大家只有自己动手,才能搞清楚这其中的机关,所谓"实践是检验C#的唯一标准",哈哈!

在C#中,你可以自由地为在派生类中为加入一个方法,或者覆盖父类的方法,如下所示,非常地简单:

[code]class Base {}
class Derived: Base
{
public void F() {}
}[/code]
和:

[code]class Base
{
public void F() {}
}
class Derived: Base
{
public void F() {}
}[/code]

好了,这一节又完了,音乐还没完,但是我真的要休息了,明天见!

小恐龙 2006-3-9 00:35
我要说的是C#中的结构(struct),注意,我在这里说的结构不是指的C#的语言结构.这里所说的是一种与类(class)相对的东西,下面我就与类相对比,来说一说这个struct.

下面的这个例子讲述了如何建立一个具有属性,方法和一个字段的结构.并讲述如何使用他.

[code]000: // Structs\struct1.cs
001: using System;
002: struct SimpleStruct
003: {
004:     private int xval;
005:     public int X
006:     {
007:         get {
008:             return xval;
009:         }
010:         set {
011:             if (value < 100)
012:                 xval = value;
013:         }
014:     }
015:     public void DisplayX()
016:     {
017:         Console.WriteLine("The stored value is: {0}", xval);
018:     }
019: }
020:
021: class TestClass
022: {
023:     public static void Main()
024:     {
025:         SimpleStruct ss = new SimpleStruct();
026:         ss.X = 5;
027:         ss.DisplayX();
028:     }
029: }[/code]
这个例子的输出是:
The stored value is: 5

从上面的例子中我们可以看到结构和类似乎是一样的.的确,如果你用类去重亲写这个程序,结果是一样的.但是,很明显,两个一样的东西是不可能一起出现的. 结构(struct)是值(value)型的,而类是参考型的.这样,你就可以用结构建立像内建类型那样的对象了.

还有就是如果你用一个new关键字建立一个类的实例的时候,它是以堆(heap)来分配的,而用new来建立一个结构的的实例的时候,它是以栈(stack)来分配.这会给我们提高很多性能(M$说的).好了,让我们再来看下面的例子吧:



[code]000: // Structs\struct2.cs
001: using System;
002:
003: class TheClass
004: {
005:     public int x;
006: }
007:
008: struct TheStruct
009: {
010:     public int x;
011: }
012:
013: class TestClass
014: {
015:     public static void structtaker(TheStruct s)
016:     {
017:         s.x = 5;
018:     }
019:     public static void classtaker(TheClass c)
020:     {
021:         c.x = 5;
022:     }
023:     public static void Main()
024:     {
025:         TheStruct a = new TheStruct();
026:         TheClass b = new TheClass();
027:         a.x = 1;
028:         b.x = 1;
029:         structtaker(a);
030:         classtaker(b);
031:         Console.WriteLine("a.x = {0}", a.x);
032:         Console.WriteLine("b.x = {0}", b.x);
033:     }
034: }[/code]
这个例子的输出是:
a.x = 1
b.x = 5

从这个例子例子可以看出,当一个结构被传递到一个方法时,被传递的只不过是一个副本,而一个类被传递时,被传递的是一个参考.所以a.x=输出的是1,不变,而b.x却变了.

还有的区别就是结构可以不用new来实例化,而类却要.如果你不用new来实例化一个结构,那么所有的字段将仍然处于未分配状态,直到所有的字段被初始化.和类一样,结构可以执行接口.更重要的是,结构没有继承性,一个结构不能从别的类继承,也不能是别的类的基类.

例三:

[code]interface IImage
{
void Paint();
}
struct Picture : IImage
{
public void Paint()
{
// painting code goes here
}
private int x, y, z;  // other struct members
}[/code]

好了,关于结构我就讲到这了,以后还会讲到的.

小恐龙 2006-3-9 00:36
大家好,我是武汉华师的SUNWEN,今天Zzz....(快醒醒!啪!..啪!倒,叫我起来也不要用皮鞋来打嘛@#$%&*).现在是五月四号晚上19:33,我好想睡觉.只好打开音乐来提神了!

这一节我要讲的是大家非常关心的,肯定也是非常感兴趣的部分.嘿嘿,也是我写教程最拿手的部分----ADO数据库访问.想到这,我就想起我在去年写的"访问数据库"系列文章,嘿嘿!所以呢,如果你觉得对记录集之类的东西比较难理解的话,我推荐你先看一看我的那几篇文章.好了,让我们开始吧!

什么是ADO(ActiveX Data Objects译作ActiveX数据对象),ADO是一个非常简单的思想,一种让你仅用一种方式去访问数据的思想.ADO不算一个新的思想,仅是采用现有的数据访问技术,将其融合起来.如果你不理解ADO,想一想ODBC吧!其实我们在搞ASP的时候,就用到了ADO,还记得吗,那个曾经被我们用过无数次的set conn=Server.CreateObject("ADODB.Connection")吗?是的,就是它.至于ADO的一些概念性的东西,请大家自行查阅资料,不过,其实不了解也没有关系,只把它想象成一个M$给我们的访问数据的工具吧!

OK,下面的例子是以一个M$ ACCESS 2000的数据库为基础的,它的结构如下,表名是Categories,文件名是BugTypes.mdb ,赶快建一个吧:

[quote]Category ID              Category Name
1                             Bugbash stuff
2                             Appweek Bugs
3                            .NET Reports
4                            Internal support [/quote]

现在我要说的是C#中的用户自定义转换(User-Defined Conversions),其中用到了前面说的struct的知识,就是结构呀,忘了吗?好,没忘就好.从我们以下的课程我们可以看到结构的用处(刚才我还在想它有什么用,呵呵).用class声明的是一个类,而用struct声明的可以看作是一个类型,对,就是像C#自带的int,short,long那样的类型了.

C#中可以允许我们对结构(struct)和类(class)进行转换,所以我们可以在其中定义一些转换.但是,C#规定,所有的转换声明都必须在显示(explicit)和隐示(implicit)中选择一个.比方说,我们用这个语句的时候
[code]int a=10;
System.Console.println(a):[/code]
就用到了int的隐示的转换toString.如果是(String)a,就叫做显示.所以,显/隐之差就在于是否表现出来.大家现在肯定还是一头雾水,等到明天我把例子写出来再分析一下就清楚了,要熄灯了,我先走一步了!


喔~~~~~终于起来了,五月五日8:45.下面给出例子,在这个例子中,一个名为RomanNumeral的类型被声明,然后对他实施了好几种转换.

[code]000: // UserConversions\conversion.cs
001: using System;
002:
003: struct RomanNumeral
004: {
005:     public RomanNumeral(int value)
006:     {
007:        this.value = value;
008:     }
009:     static public implicit operator RomanNumeral(int value)
010:     {
011:        return new RomanNumeral(value);
012:     }
013:     static public explicit operator int(RomanNumeral roman)
014:     {
015:        return roman.value;
016:     }
017:     static public implicit operator string(RomanNumeral roman)
018:     {
019:        return("Conversion not yet implemented");
020:     }
021:     private int value;
022: }
023:
024: class Test
025: {
026:     static public void Main()
027:     {
028:         RomanNumeral numeral;
029:
030:         numeral = 10;
031:
032: // 显式地从numeral到int的转换
033:         Console.WriteLine((int)numeral);
034:
035: // 隐示地转换到string
036:         Console.WriteLine(numeral);
037:  
038: // 显示地转换到int,然后显示地转换到short
040:         short s = (short)numeral;
041:
042:         Console.WriteLine(s);
043:
044:     }
045: }[/code]
这个例子子的输出是:

10
Conversion not yet implemented
10
注意009和013的operator操作符,它是一个转换操作符.static public explicit operator int(RomanNumeral roman),记住这样的形式,它就代表了一个转换.再看第033行,因为在前面int这个转换被声明成了explicit,即显示地,所以,在使用这个转换时,必须用括号.

下面再给出一个例子,这个例子声明了两个结构,RomanNumeral和BinaryNumeral,然后在它们之间进行转换.

[code]000: // UserConversions\structconversion.cs
001: using System;
002:
003: struct RomanNumeral
004: {
005:     public RomanNumeral(int value) { this.value = value; }
006:     static public implicit operator RomanNumeral(int value)
007:         {return new RomanNumeral(value);}
008:     static public implicit operator
009:         RomanNumeral(BinaryNumeral binary)
010:             {return new RomanNumeral((int)binary);}
011:     static public explicit operator int(RomanNumeral roman)
012:         {return roman.value;}
013:     static public implicit operator string(RomanNumeral roman)
014:         {return("Conversion not yet implemented");}
015:     private int value;
016: }
017:
018: struct BinaryNumeral
019: {
020:     public BinaryNumeral(int value) {this.value = value;}
021:
022:     static public implicit operator BinaryNumeral(int value)
023:         {return new BinaryNumeral(value);}
024:     static public implicit operator string(BinaryNumeral binary)
025:         {return("Conversion not yet implemented");}
026:     static public explicit operator int(BinaryNumeral binary)
027:         {return(binary.value);}
028:
029:     private int value;
030: }
031:
032: class Test
033: {
034:     static public void Main()
035:     {
036:         RomanNumeral roman;
037:         roman = 10;
038:         BinaryNumeral binary;
039:         binary = (BinaryNumeral)(int)roman;
040:         roman = binary;
041:         Console.WriteLine((int)binary);
042:         Console.WriteLine(binary);
043:     }
044: }[/code]这个例子的输出是:

10
Conversion not yet implemented
注意,第039行并没有直接由RomanNumeral转化成BinaryNumeral,因为没有直接的转换提供.所以先把RomanNumeral转换成int,再转成BinaryNumeral.其余的东西跟上面的例子是一样的(至少我这么认为),如果上面的例子理解了,下面的就好了.

小恐龙 2006-3-9 00:38
现在我想说的是C#中的容器.这是一个非常重要的话题,因为不管你写什么样的程序,你都不能不与容器打交道.什么是容器呢(倒!).容器就是可以容纳东西的东西(再倒!),在C#和JAVA这种面向对象的编程语言中,容器就被称为可以容纳对象的东东,不是说"一切都是对象吗?"以前,我一个搞C++的程序员朋友告诉我,JAVA中的容器太好用了,比C++好用多了.而作为JAVA的后来者的C#毫无疑问,它的容器功能肯定也是很强大的.

foreach语句是遍历容器的元素的最简单的方法.我们可以用System.Collections.IEnumerator类和System.Collections.IEnumerable接口来使用C#中的容器,下面有一个例子,功能是字符串分割器.

[code]000: // CollectionClasses\tokens.cs
001: using System;
002: using System.Collections;
003:
004: public class Tokens : IEnumerable
005: {
006:    private string[] elements;
007:
008:    Tokens(string source, char[] delimiters)
009:    {
010:       elements = source.Split(delimiters);
011:    }
012:
013:    //引用IEnumerable接口
014:
015:    public IEnumerator GetEnumerator()
016:    {
017:       return new TokenEnumerator(this);
018:    }
019:
020:   
021:
022:    private class TokenEnumerator : IEnumerator
023:    {   
024:       private int position = -1;
025:       private Tokens t;
026:
027:       public TokenEnumerator(Tokens t)
028:       {
029:          this.t = t;
030:       }
031:
032:       public bool MoveNext()
033:       {
034:          if (position < t.elements.Length - 1)
035:          {
036:             position++;
037:             return true;
038:          }
039:          else
040:          {
041:             return false;
042:          }
043:       }
044:
045:       public void Reset()
046:       {
047:          position = -1;
048:       }
049:
050:       public object Current
051:       {
052:          get
053:          {
054:             return t.elements[position];
055:          }
056:       }
057:    }
058:
059:    // 测试
060:
061:    static void Main()
062:    {
063:       Tokens f = new Tokens("This is a well-done program.", new char[] {' ','-'});
064:       foreach (string item in f)
065:       {
066:          Console.WriteLine(item);
067:       }
068:    }
069: }[/code]

这个例子的输出是:
This
is
a
well
done
program.
好了,这一节就说到这了.现在环境不太好,旁边一大帮同学在看VCD,不好搞.




OK,我又回来了,前面写了那么多,跟JAVA的对比也够多了,现在我就用我自己试验的例子,来看一看C#这个被称为JAVA#的新一代编程语言到底与JAVA有多大的区别.

首先我们建立一个C#的程序文件cs1.cs,然后再建立一个JAVA的源程序文件cs1.java.它们的内容分别是:

[code]cs1.cs:

using System;
public class cs1{
public static void Main(){
Console.WriteLine("i am sunwen!");
sunwen mySunwen=new sunwen();
Console.WriteLine(mySunwen.name);
}
}
class sunwen{
public String name="chenbin!";
}

cs1.java:

import System.*;
public class cs1{
public static void main(String args[]){
System.out.println("i am sunwen,how are you!");
sunwen mySunwen=new sunwen();
System.out.println(mySunwen.name);
}
}
class sunwen{
String name="chenbin!";
}[/code]

OK,让我们来运行一下这两个程序.在编译过程中我们发现,C#的速度的确比JAVA要快好多.(不是说M$的好话)其实,两个程序的输出是一样的,都是:

i am sunwen!
chenbin!

有一个重要的区别就是看有一行是publc String name="chenbin!",而在JAVA中却是String name="chenbin!".如果我们在cs1.cs中把这个public去掉,就会产生错误,因为在C#中,不用任何范围修饰符时,默认的是protect,因而不能在类外被访问.

这是一个重要的区别之一.还有就是:如果我们把cs1.cs这个C#程序中的一句public class cs1改为public class cs2,存盘,再编译,可以看到,程序正常运行.而在JAVA中,这显然不行,因为JAVA规定,在一个文件中只能有一个public类,而且这个类的名称必须与文件名一模一样.这又是一个区别,在C#中,它是以Main方法来定位入口的.如果一个程序中没有一个名为Main的方法,就会出"找不到入口的错误".不要把Main写成main哟,嘻嘻,我经常犯这样的错误.

好了,就说到这了,以后再遇到再说吧!


查看完整版本: C#进阶[转贴]


Powered by Discuz! Archiver 4.0.0  © 2001-2005 Comsenz Technology Ltd