实现流操作
Java和C#中的流数据非常地相似。如果对Java中的流操作足够熟悉,在C#中完成流操作就不会是太困难的事儿了。Java和C#中实现流操作的最主要的差别不仅仅是需要实现的适当的读或写方法,还有C#中的Stream类还可以作为"读者"或"作者"和准确反映Stream容量的属性。
表2:Java和C#中Stream操作的实现
Java C#
读:
用java.io.InputStream中的read()方法实现,也可以用InputStream中的其他二个read方法实现
使System.IO.Stream中的CanRead属性的值为真,至少可以使用Read、ReadByte或BeginRead/EndRead方法之一实现。
定位:
java.io.InputStream中的skip方法可以用来在文件中向前移动指针,也可以使用markSupported、mark和reset方法在流中向后移动指针。
使用System.IO.Stream的CanSeek属性通知程序,该流可以进行定位操作,使用Seek方法完成定位操作。
写:
使用java.io.OutputStream中的write方法实现写操作(还可以使用OutputStream中的其他write方法完成写操作。)
从System.IO.Stream中的CanWrite属性返回真值后,至少可以使用Write、WriteByte或BeginWrite/EndWrite三种方法之一完成写操作。
C#中的Stream类在完成某一种功能时提供了多种选择,最重要的Read和Write(二者都需要一个字节数组、偏移量、长度三个参数)这二个方法就足够了,因为所有方法的执行都会用到其他的方法,简单的read/write方法将能够给流添加所需要的功能。缺省的ReadByte和WriteByte的执行方式将把long型变量与字节数组之间进行转换,异步执行的BeginRead和BeginWrite方法将在独立的线程中执行Read或Write方法。
这篇稿子的大部分篇幅谈论的都是C#中的System.IO.Stream类有关,在这里我们还需要讨论一下System.IO.TextReader和System.IO.TextWriter这二个类,这二个类与Java处理I/O的方式十分相似,其中的一个类负责处理读取操作,另一个类则负责处理写操作。C#中的Stream对象掩盖了如何读取、写的有关细节,TextReader和TextWriter则分别独立地存储着读取或写入的字节。由上述二种类衍生出的最通用的类是System.IO.StreamReader和System.IO.StreamWrtiter类,这二个类都可以对一个Stream对象进行操作,System.Text.Encoding对象指定一个字节流如何转换为字符流。(缺省情况下,C#使用UTF-8进行编码/解码。)
如果需要使用与流类似的功能,而且需要对字符而不是字节进行操作,使用TextReader和TextWriter二个类的子类要比使用Stream类简单得多。虽然如果Stream动用得当,也可以使用StreamReader和StreamWriter类实现流操作。
文件系统I/O
在Java中完成磁盘操作是十分简单的,通常情况下它就是操作java.io.File对象和使用java.io.FileInputStream或java.io.FileOutputStream。跟我们在上面看到的一样,C#在许多方面与Java相同,但也有一些不同之处。
与Java一样,C#的文件对象与底层的文件系统之间并没有特别紧密的联系。我们可以为一个不存在的文件创建一个File对象,也可以为存在的文件创建一个File对象,并在C#不知道的情况下将它移到别处。由于拥有CreateText或AppendText等向文件系统返回一个流的静态方法,因此,与Java不同的是,C#中的File对象的作用要重要得多。
在Java中要创建一个新文件或向文件写内容,则必须使用FileInputStream:
FileOutputStream fos = new FileOutputStream( "brand-new-file.txt");
fos.write( ... )
但在C#中就可以使用:
Stream s = File.Create( "brand-new-file.txt" );
或者使用:
StreamWriter sw = File.CreateText( "brand-new-file.txt" );
来得到一个新文件的Stream或StreamWriter。(Java中的添加可以通过设置FileOutputStream的构建者之一的append属性来完成。)Java允许使用java.io.FileInputStream从文件中读取内容,而C#则有Open Write和OpenText等静态的方法。最后,C#在Open方法中提供了更详细的控制━━这一方法允许设置文件的权限和访问环境。
表3:操作文件读写的方法
Java C#
创建新文件:
使用java.io.FileOutputStream
使用静态的File.Create方法、静态CreateText或实例方法CreateText
向文件写:
使用java.io.FileOutputStream。
使用静态或实例的OpenWrite方法。
向文件添加文本:
使用java.io.FileOutputStream,但需要使用带append参数的方法。
使用静态或实例的AppendText方法。
从文件读取文本:
使用java.io.FileOutputStream。
命运动态或实例的OpenRead、OpenText方法。
C#中值得一提的改进是File.Copy方法。困扰大多数Java编程人员的一个与文件系统I/O有关的问题是不能正确地移动文件,java.io.File中包含一个可以对文件重新命名的renameTo方法,但它只在文件系统内有效。大多数情况下,编程人员都必须编写自己的文件移动命令,一般是利用java.io.FileInputStream和java.io.FileOutputStream拷贝文件,然后再删除原来的文件。C#中的Copy方法可以很方便地移动文件,但File.Move也不能跨卷和文件系统移动文件。
C#的文件系统无须处理Java必须处理的跨平台问题,因此也就没有与java.io.File.pathSeparator或java.io.File.separator功能类似的对象。不幸的是,这也就意味着在C#中不存在受到广大编程人员喜爱的java.io.File构造器:
public File( File parent, String child )
使用C#的编程人员可以使用下面的命令来构造一个新的System.IO.File对象:
File parent ...
File child new File( parent.FullName + "\\" + childName );
Understanding Networking
二种语言都围绕着基本协议提供了一些抽象层,Java中的java.net.Socket类的抽象程度要远高于C#中的System.Net.Sockets.Socket类。
Java和C#都提供了对网络的不同抽象层,编程人员可以使用不同的网络接口完成对网络的操作。
表4:Java和C#中的网络层次
Java C#
应答/请求:
java.net.URL和java.net.URLConnection。
System.Net.WebRequest。
协议:
TCP/IP协议使用java.net.Socket和java.net.ServerSocket;
UDP协议使用java.net.DatagramSocket和java.net.MulticastSocket。
CP/IP协议使用System.Net.Sockets.TCPListener和System.Net.Sockets.TCPClient;
UDP协议使用TSystem.Net.Sockets.UDPClient
原始套接字层:
没有。
System.Net.Sockets.Socket
应答/请求层可以用于HTTP类的请求,其中的一端开始启动一个连接,发送一些字节的数据,然后停止,等待对方作为应答发回的一些字节。对于象流这样更灵活的操作,协议层的用处更大。对于大多数的Java编程人员来说,除非需要完成性能非常高的网络操作,不需要对套接字进行直接控制。如果需要。C#仍然提供了对原始的Berkeley套接字进行控制的能力。