|
|
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分,我要休息一下了!哈哈!待会见! |
|
|
这次我要向大家讲的是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++编程经验的朋友.不过不要害怕,鼓起勇气学下去,一定会有所收获的. 我也要休息一下了,嘿嘿,早饭还没有吃呢! |
|
|
我现在要说的是库(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,明白了吧,呵呵.
好了,又完了一篇,今天的任务完成了,可以休息了.唉,有什么好玩的呢? |
|
|
现在我要说的是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]
好了,这一节又完了,音乐还没完,但是我真的要休息了,明天见! |
|
|
我要说的是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]
好了,关于结构我就讲到这了,以后还会讲到的. |
|
|
大家好,我是武汉华师的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.其余的东西跟上面的例子是一样的(至少我这么认为),如果上面的例子理解了,下面的就好了. |
|
|
现在我想说的是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哟,嘻嘻,我经常犯这样的错误.
好了,就说到这了,以后再遇到再说吧! |
Powered by Discuz! Archiver 4.0.0 © 2001-2005 Comsenz Technology Ltd |