FileStream 继承于 Stream,所以你可以用一个 StreamReader 对象把 FileStream 对象包装起来。这样为一行一行地进行流处理提供了一个良好的界面:
StreamReader sr = new StreamReader( fs );
string curLine;
while( (curLine = sr.ReadLine()) != null )
Console.WriteLine( curLine );
最后关闭 StreamReader 对象:
sr.Close();
注意这样将自动地在底层 Stream 对象上调用 Close (),所以不必显示地执行 fs.Close()。
11.1.2 如何写文本文件?
和读文件的例子相似,只是把 StreamReader 换成 StreamWriter。
11.1.3 如何读写二进制文件?
和文本文件类似,只是要用 BinaryReader/Writer 对象而不是 StreamReader/Writer 来包装 FileStream 对象。
11.1.4 如何删除文件?
在 System.IO.File 对象上使用静态方法 Delete ():
File.Delete( @"c: est.txt" );
11.2 文本处理
11.2.1 是否支持正规表达式?
是的。使用 System.Text.RegularExpressions.Regex 类。例如,以下代码更新 HTML 文件的标题:
FileStream fs = new FileStream( "test.htm", FileMode.Open, FileAccess.Read );
StreamReader sr = new StreamReader( fs );
Regex r = new Regex( "<TITLE>(.*)</TITLE>" );
string s;
while( (s = sr.ReadLine()) != null )
{
if( r.IsMatch( s ) )
s = r.Replace( s, "<TITLE>New and improved ${1}</TITLE>" );
Console.WriteLine( s );
}
11.3 Internet
11.3.1 如何下载网页?
首先使用 System.Net.WebRequestFactory 类来获得一个 WebRequest 对象:
WebRequest request = WebRequestFactory.Create( "http://localhost" );
然后请求应答:
WebResponse response = request.GetResponse();
GetResponse 方法被阻塞直到下载完成。然后你能像下面那样访问应答流:
Stream s = response.GetResponseStream();
// Output the downloaded stream to the console
StreamReader sr = new StreamReader( s );
string line;
while( (line = sr.ReadLine()) != null )
Console.WriteLine( line );
注意 WebRequest 和 WebReponse 对象分别向下兼容 HttpWebRequest 和 HttpWebReponse 对象,它们被用来访问和 http 相关的功能。
11.3.2 如何使用代理服务器 (proxy)?
两种?这样做以便影响所有 Web 请求:
System.Net.GlobalProxySelection.Select = new DefaultControlObject( "proxyname", 80 );
另外一种,要想对特定的 Web 请求设置代理服务,这样做:
ProxyData proxyData = new ProxyData();
proxyData.HostName = "proxyname";
proxyData.Port = 80;
proxyData.OverrideSelectProxy = true;
HttpWebRequest request = (HttpWebRequest)WebRequestFactory.Create( "http://localhost" );
request.Proxy = proxyData;
11.4 XML
11.4.1 是否支持 DOM?
是的。看看以下示例 XML文档:
<PEOPLE>
<PERSON>Fred</PERSON>
<PERSON>Bill</PERSON>
</PEOPLE>
可以这样处理此文档:
XmlDocument doc = new XmlDocument();
doc.Load( "test.xml" );
XmlNode root = doc.DocumentElement;
foreach( XmlNode personElement in root.ChildNodes )
Console.WriteLine( personElement.FirstChild.Value.ToString() );
输出为:
Fred
Bill
11.4.2 是否支持 SAX?
不。作为替换,提供了一个新的 XmlReader/XmlWriter API。像 SAX 一样,它是基于流的,但它使用“pull”模型而不是 SAX 的“push”模型。这是一个例子:
XmlTextReader reader = new XmlTextReader( "test.xml" );
while( reader.Read() )
{
if( reader.NodeType == XmlNodeType.Element && reader.Name == "PERSON" )
{
reader.Read(); // Skip to the child text
Console.WriteLine( reader.Value );
}
}
11.4.3 是否支持 XPath?
是的,通过 XmlNavigator 类 (DocumentNavigator 是从 XmlNavigator 导出的):
XmlDocument doc = new XmlDocument();
doc.Load( "test.xml" );
DocumentNavigator nav = new DocumentNavigator(doc);
nav.MoveToDocument();
nav.Select( "descendant::PEOPLE/PERSON" );
while( nav.MoveToNextSelected() )
{
nav.MoveToFirstChild();
Console.WriteLine( "{0}", nav.Value );
}
11.5 线程
11.5.1 是否支持多线程?
是的,对多线程有广泛的支持。系统能产生新线程,并提供应用程序可以使用的线程池。
11.5.2 如何产生一个线程?
创建 System.Threading.Thread 对象的一个实例,把将要在新线程中执行的 ThreadStart 示例传递给它。例如:
class MyThread
{
public MyThread( string initData )
{
m_data = initData;
m_thread = new Thread( new ThreadStart(ThreadMain) );
m_thread.Start();
}
// ThreadMain() is executed on the new thread.
private void ThreadMain()
{
Console.WriteLine( m_data );
}
public void WaitUntilFinished()
{
m_thread.Join();
}
private Thread m_thread;
private string m_data;
}
这里创建 MyThread 的一个实例就足以产生线程并执行 MyThread.ThreadMain () 方法:
MyThread t = new MyThread( "Hello, world." );
t.WaitUntilFinished();
11.5.3 如何停止一个线程?
有好几个办法。首先,你能使用自己的通讯机制告诉 ThreadStart 方法结束。另外 Thread 类有内置的支持来命令线程停止。基本的两个方法是 Thread.Interrupt () 和 Thread.Abort ()。前者导致抛出一个 ThreadInterruptedException 并随后进入 WaitJoinSleep 状态。换句话说,Thread.Interrupt 是一种礼貌的方式,它请求线程在不再进行任何有用的工作时自行停止的。与此相对应,Thread.Abort () 抛出一个 ThreadAbortException 而不管线程正在做什么。而且,ThreadAbortException 不能像通常的异常那样被捕获 (即使最终将执行 ThreadStart 的终止方法)。Thread.Abort () 是一般情况下不需要的非常手段。
11.5.4 怎样使用线程池?
通过向 ThreadPool.QueueUserWorkItem () 方法传递 WaitCallback 的一个实例:
class CApp
{
static void Main()
{
string s = "Hello, World";
ThreadPool.QueueUserWorkItem( new WaitCallback( DoWork ), s );
Thread.Sleep( 1000 ); // Give time for work item to be executed
}
// DoWork is executed on a thread from the thread pool.
static void DoWork( object state )
{
Console.WriteLine( state );
}
}
11.5.5 怎样知道我的线程池工作项目是在何时完成的?
没有方法询问线程池这类信息。你必须在 WaitCallback 方法中放置代码来发出信号以表明它已经完成。这里事件也很有用处。
11.5.6 怎样防止对数据的并发访问?
每个对象有一个与之相联的并发锁 (受批评的部分)。System.Threading.Monitor.Enter/Exit 方法用来获得和释放锁。例如,下面类的实例只允许一个线程同时进入方法 f ():
class C
{
public void f()
{
try
{
Monitor.Enter(this);
...
}
finally
{
Monitor.Exit(this);
}
}
}
C# 有一个关键字‘lock’提供了以上代码的简单形式:
class C
{
public void f()
{
lock(this)
{
...
}
}
}
注意,调用 Monitor.Enter (myObject) 并不意味着对 myObject 的所有访问都被串行化了。它意味着请求同 myObject 相联的同步锁,并且在调用 Monitor.Exit(o) 之前,没有任何其它线程可以请求该锁。换句话说,下面的类和以上给出的类在功能上是等同的:
class C
{
public void f()
{
lock( m_object )
{
...
}
}
private m_object = new object();
}
11.6 跟踪
11.6.1 有内置的跟踪/日志支持吗?
是的,在 System.Diagnostics 命名空间中。有两个处理跟踪的主要的类?Debug 和 Trace。它们以相似的方式工作?不同之处是 Debug 类中的跟踪只能在用 DEBUG 标志生成的代码中工作,而 Trace 类中的跟踪只能在指明了 TRACE 标记生成的代码中工作。典型地,这意味着你应该在你希望能在 debug 和 release 版本中都能跟踪时使用 System.Diagnostics.Trace.WriteLine,而在你希望只能在 debug 版本中能跟踪时使用 System.Diagnostics.Debug.WriteLine。
11.6.2 能否将跟踪输出重定向到一个文件?
是的。Debug 类和 Trace 类都有一个 Listeners 属性,它们分别收集你用 Debug.WriteLine 或 Trace.WriteLine 产生的输出。默认情况下 Listeners 只有一个收集槽,它是 DefaultTraceListener 类的一个实例。它将输出发送到 Win32 的 OutputDebugString () 函数和 System.Diagnostics.Debugger.Log () 方法。调试时这很有用,但如果你试图从客户站点跟踪一个问题,将输出重定向到一个文件中就更为恰当。幸运的是,为此目的提供了 TextWriterTraceListener 类。
这里是 TextWriterTraceListener 如何将 Trace 输出重定向到一个文件:
Trace.Listeners.Clear();
FileStream fs = new FileStream( @"c:log.txt", FileMode.Create, FileAccess.Write );
Trace.Listeners.Add( new TextWriterTraceListener( fs ) );
Trace.WriteLine( @"This will be writen to c:log.txt!" );
注意使用 Trace.Listeners.Clear () 去掉了默认的 listener。如果不这样做,输出将在文件和 OutputDebugString () 中同时产生。一般情况下你不希望如此,因为 OutputDebugString () 导致很大的性能开销。
11.6.3 能否定制跟踪的输出?
是的。你能编写你自己的 TraceListener 导出类,并把所有的输出重定向到它上面。这里有一个简单的例子,它从 TextWriterTraceListener 导出 (并随后内建了对写文件的支持) 并在每个输出行上添加时间信息和线程 ID:
class MyListener : TextWriterTraceListener
{
public MyListener( Stream s ) : base(s)
{
}
public override void WriteLine( string s )
{
Writer.WriteLine( "{0:D8} [{1:D4}] {2}",
Environment.TickCount - m_startTickCount,