>样本章节

实现数据访问

在本示例章节中,C_中的Exam Ref 70-483编程,第二版,learn how C# programs store and manipulate data,review the use of Language Integrated Query (LINQ),并探索.NET框架提供的数据收集工具。

A program can use variables to store values as it is running,但它需要一种方法来保存数据。We have already touched on data storage at several points in this book,在技能3.1中选择适当的数据收集类型,“我们考虑了如何最好地将应用程序中的信息映射到C提供的数据类型。在本节后面”使用JSON,“我们看到了如何序列化对象中的数据。And finally,in the"Use the Entity Framework to design your data storage"我们创建的部分是自动映射到数据库表中以供应用程序使用的类。在阅读本章之前,这些话题值得你仔细研究一下,让它们在你的头脑中保持新鲜。

In this chapter we're going to bring all of these different data storage and access abilities together and consider how a C# program can store and manipulate data;从使用和管理文件存储开始,然后再转移到数据库中,然后介绍了语言集成查询(LINQ)的使用。然后我们将详细了解序列化,最后探讨.NET框架提供的数据收集工具。

技能4.1:执行I/O操作

在本节中,我们将介绍应用程序中数据存储的基础输入/输出(I/O)操作。您将发现操作系统和.NET框架库如何管理文件,这些库允许程序存储和加载数据。与现代处理器的速度相比,文件访问是一种非常缓慢的活动,因此,我们还将研究异步I/O的使用,即使在读取或写入大量数据时,也可以使用它来保持应用程序的响应。

读写文件和流

A stream is a software object that represents a stream of data..NET框架提供了河流类,用作可用于读取和写入数据的一系列类的父类型。程序可以通过三种方式与流进行交互:

  • 将字节序列写入流

  • Read a sequence of bytes from a stream

  • 定位“文件指针在小溪中

文件指针是流中下次执行读或写操作的位置。A program can use the寻找流提供的用于设置此位置的方法。例如,a program can search through a file stream connected to formatted records for one that has a particular name.

The河流类是抽象的,用作连接到实际存储资源的流的模板。It is a very good example of how C# classes can be used to deploy resources.任何可以与河流可以处理任何行为类似于流的对象。在清单3-24的第3章中,我们使用了内存流将加密过程的输出捕获到字节数组中。通过使用不同类型的流对象,可以很容易地将该程序中生成的加密数据重定向到文件或网络连接。Figure 4-2shows how the系统IO流类型是提供不同形式流连接的大量类的基类型。

图4-1

图4-1Some of the Stream types

The child classes all contain the stream behaviors that allow data to be transferred,例如河流methodWrite可在其中任何一个上用于向该流写入字节。However,创建的每种类型的流如何依赖于该流类型。例如,创建一个文件流程序必须指定文件的路径以及如何使用该文件。创建一个内存流程序必须在内存中指定要使用的缓冲区。

使用文件流

The文件流对象提供连接到文件的流实例。流对象实例将对流的调用转换为运行程序的计算机上文件系统的命令。文件系统为执行计算机数据存储的物理设备提供接口。Figure 4-2显示了这是如何工作的。一个电话的Write流对象中的方法将生成一个请求,请求文件系统将数据写入存储设备。

图4-2

图4-2流对象

清单4-1显示了程序如何使用文件流创建连接到新文件或现有文件的输出流。程序向该流写入一个byes块。It then creates a new stream that is used to read the bytes from the file.要写入的字节是通过对文本字符串进行编码获得的。

清单4-1使用文件流

使用系统;使用system.io;使用system.text;命名空间列表_4_1_using_a_filestream class program_static void main(string[]args)//写入文件filestream outputstream=new filestream(“OutputText.txt",文件格式。OpenReCube,fileaccess.write);string outputMessageString=“Hello world";byte[]outputmessagebytes=编码.utf8.getbytes(outputmessagestring);outputStream.Write(outputMessageBytes,0,outputMessageBytes.Length);outputstream.close();filestream inputstream=新文件流(“OutputText.txt",FileMode.Open,FileAccess.Read)            long fileLength = inputStream.Length;byte[] readBytes = new byte[fileLength];inputStream.Read(readBytes,0,(int)文件长度;string readstring=encoding.utf8.getstring(readbytes);inputstream.close();Console.WriteLine("阅读信息:0”,readString);Console.ReadKey();} }

控制文件与文件模式和文件访问一起使用

A stream can be associated with reading,写作,或更新文件。底座河流类提供程序可用于确定给定流实例的能力的属性(程序是否可以读取,写,或者在这条小溪上寻找。

The文件格式枚举用于文件流指示如何打开文件。The following modes are available:

  • FileMode.AppendOpen a file for appending to the end.如果文件存在,将查找位置移动到此文件的结尾。如果文件不存在;create it.只有在打开文件进行写入时才能使用此模式。

  • 创建文件Create a file for writing.如果文件已经存在,它是覆盖。请注意,这意味着文件的现有内容将丢失。

  • FileMode.CreateNewCreate a file for writing.如果文件已经存在,引发异常。

  • FileMode.Open打开现有文件。如果文件不存在,则引发异常。此模式可用于读或写。

  • 文件模式.打开或创建打开一个文件进行读写。If the file does not exist,an empty file is created.此模式可用于读或写。

  • 文件模式.truncate打开要写入的文件并删除任何现有内容。

TheFileAccess枚举用于指示如何使用文件。The following access types are available:

  • 文件访问打开一个文件进行阅读。

  • 文件访问.readwrite打开一个文件进行读写。

  • FileAccess.Write打开一个文件进行写入。

You can see these used in Listing 4-1.如果文件流的使用方式与文件流的打开方式不兼容,操作将失败,并出现异常。

使用Unicode将文本转换为二进制数据

流只能在存储设备之间传输字节数组,因此清单4-1中的程序使用编码类从System.Text命名空间。TheUTF8property of this class provides methods that will encode and decode Unicode text.We looked at Unicode in the section"字符串比较和文化,“in Skill 2.7.

Unicode is a mapping of character symbols to numeric values.TheUTF8编码将Unicode字符映射到可以存储在字节数组中的8位值上。大多数文本文件是使用UTF8编码的。The Encoding class also provides support for other encoding standards including UTF32 (Unicode encoding to 32-bit values) and ASCII.

The获取字节encoding method takes a C# string and returns the bytes that represent that string in the specified encoding.The获取字符串decoding method takes an array of bytes and returns the string that a buffer full of bytes represents.

IDispose和文件流对象

The河流类实现不可分的界面如技能2.4所示。This means that any objects derived from the河流类型还必须实现接口。这意味着我们可以使用C使用构造以确保不再需要时关闭文件。清单4-2显示了这是如何工作的。

清单4-2文件流和IDisposable

using (FileStream outputStream = new FileStream("OutputText.txt",FileMode.OpenOrCreate,fileaccess.write))string outputmessageString=“Hello world";byte[]outputmessagebytes=编码.utf8.getbytes(outputmessagestring);outputStream.Write(outputMessageBytes,0,outputMessageBytes.Length);}

使用文本文件

The filesystem makes no particular distinction between文本文件和二元的files.We have already seen how we can use the编码class to convert Unicode text into an array of bytes that can be written into a binary file.However,the C# language provides stream classes that make it much easier to work with text.TheTextWriterandTextReader类是抽象类,用于定义一组可与文本一起使用的方法。

The流写器class extends theTextWriterclass to provide a class that we can us to write text into streams.清单4-3显示了流写器and流读出器classes can be used work with text files.它执行与清单4-1中的程序相同的任务,but it is much more compact.

LISTING 4-3 StreamWriter and StreamReader

using (StreamWriter writeStream = new StreamWriter("OutputText.txt")writestream.write(“Hello world";}using (StreamReader readStream = new StreamReader("OutputText.txt"))string readstring=readstream.readtoend();Console.WriteLine("文本读取:{ 0 },读字符串);}

Chain streams together

The河流class has a constructor that will accept another stream as a parameter,允许创建流链。清单4-4显示了如何使用GZIPFISH系统IO压缩将保存和加载压缩文本的流链中的命名空间。

清单4-4存储压缩文件

using (FileStream writeFile = new FileStream("“压缩”,FileMode.OpenOrCreate,fileaccess.write))使用(gzipstream writefilezip=new gzipstream(writefile,compressionMode.compress)使用(streamwriter writefiletext=new streamwriter(writefilezip))writefiletext.write(“Hello world";使用(文件流读取文件=新文件流(”“压缩”,FileMode.Open,fileaccess.read))使用(gzipstream readfilezip=new gzipstream(readfile,compressionMode.decompress))使用(streamreader readfiletext=new streamreader(readfilezip))string message=readfiletext.readtoend();Console.WriteLine("读取文本:{ 0 },消息);} }

Use the File class

TheFile类是一个“帮手类,以便更轻松地处理文件。It contains a set of static methods that can be used to append text to a file,copy a file,创建文件,删除文件,move a file,open a file,读取文件,管理文件安全。Listing 4-5 shows some of the features of theFile行动类。

清单4-5文件类

file.writealltext(路径:“TextFile.txt",contents:"此文本将进入文件“;File.AppendAllText(path:"TextFile.txt",contents:"-这就结束了”;if (File.Exists("TextFile.txt"))    Console.WriteLine("文本文件存在“;string contents=file.readalltext(路径:“TextFile.txt";Console.WriteLine("File contents: {0}",内容);file.copy(源文件名:“TextFile.txt",destFileName:"copytextfile.txt“;使用(textReader reader=file.opentext(路径:“copytextfile.txt“))字符串文本=reader.readtoend();Console.WriteLine("Copied text: {0}",text);}

处理流异常

Exceptions are situations where it is not meaningful for a given thread of execution to continue.线程可以引发异常并将控制权传递给处理程序,该处理程序将尝试以合理的方式解决这种情况。You first saw exceptions in Skill 1.5,“实现异常处理。”When creating applications that use streams you need to ensure that your code can deal with any exceptions that might be thrown by the stream.这些可以在使用流的任何时间发生。Our application may try to open a file that does not exist,或者给定的存储设备在写入时可能会满。It is also possible that threads in a multi-threaded application can"“战斗”over files.如果一个线程试图访问另一个线程已在使用的文件,this will lead to exceptions being thrown.

考虑到这一点,您应该确保打开和与流交互的生产代码受到以下保护:try–catchconstructions.有一组文件异常用于指示不同的错误条件。Listing 4-6 shows how a program can detect theFileNotFoundException并以不同的方式响应其他文件异常。

LISTING 4-6 Stream exceptions

Try String contents=file.readAllText(路径:“TestFr.txt;Console.WriteLine(contents);catch(filenotfoundexception notfoundex)//找不到文件console.writeline(notfoundex.message);}catch(Exception ex){    // Any other exception    Console.WriteLine(ex.Message);}

文件存储

给定的存储设备,可能是磁盘驱动器或USB便携式磁盘,can be divided into隔板.Each partition represents an area on the storage device that can be used to store data.存储设备上的分区公开为开车哪一个,在Windows操作系统上,表示为驱动器字母。驱动器号由操作系统分配,并用作absolute pathto a file on the computer.

The Disk Management application allows administrators to re-assign drive letters,将多个物理驱动器合并为一个逻辑驱动器,并连接从驱动器映像创建的虚拟硬盘驱动器(VHD)。图4-3below shows this program in use.

FIGURE 4-3

FIGURE 4-3磁盘管理程序

Each of the partitions on a physical storage device is格式化的使用一个特定的归档系统管理文件存储的。清单4-7中的程序显示了驾驶信息类中System.IO命名空间可用于获取有关连接到系统的驱动器的信息。

清单4-7驱动器信息

driveinfo[]drives=driveinfo.getdrives();foreach (DriveInfo drive in drives){    Console.Write("姓名:{ 0 },驱动器;名称;if(drive.isready)console.write(“Type:{0}",驱动。驱动类型);控制台。格式:{ 0 },驱动器。驱动器格式);控制台。Free space:{0}",驱动器。完全可用空间);else console.write(“Drive not ready";}    Console.WriteLine();}

当我运行程序时,创建了以下输出:

名称:C:\type:fixed format:ntfs free space:69709230080名称:D:\type:fixed format:ntfs free space:133386022912名称:E:\drive not ready name:F:\type:removable format:exfat free space:41937928192name:G:\type:fixed format:ntfs free space:44650496名称:K:\drive not ready name:L:\drive not ready

Note that some of the drive letters have been allocated to removable devices.驱动器F是相机的存储卡。The drives that are shown as not ready do not presently have devices physically connected.

使用文件信息

文件系统维护其存储的每个文件的信息。这包括文件名,与文件关联的权限,与创建关联的日期,修改文件,以及文件在存储设备上的物理位置。文件系统还维护每个文件的属性信息。属性信息被保存为一个值,该值中的不同位表示不同的属性。We can use logical operators to work with these values and assign different attributes to a file.可用属性如下:

  • FileAttributes.Archive文件尚未备份。当/如果备份文件时,该属性将被清除。

  • 文件属性。压缩文件被压缩。This is not something that our program should change.

  • 文件属性.directory文件是一个目录。This is not something our program should change.

  • 文件属性。隐藏The file will not appear in an ordinary directory listing.

  • 文件属性.normal这是一个没有特殊属性的普通文件。仅当没有其他属性分配给文件时,此属性才有效。

  • 文件属性.readonly无法写入文件。

  • 文件属性.system该文件是操作系统的一部分,由它使用。

  • 文件属性。临时该文件是一个临时文件,当应用程序完成时不需要该文件。文件系统将尝试将此文件保存在内存中以提高性能。

这些信息通过文件信息班级。Listing 4-8 shows how a program can obtain the文件信息有关文件的信息,然后使用属性信息。The program creates a new file and then obtains the文件信息表示文件的对象。它使用属性性质文件信息对象以生成文件readOnly然后移除readOnly属性。

LISTING 4-8 Using FileInfo

字符串文件路径=“TextFile.txt";File.WriteAllText(path: filePath,contents:"此文本将进入文件“;fileinfo info=new fileinfo(filepath);Console.WriteLine("Name: {0}",信息;姓名;Console.WriteLine("Full Path: {0}",信息;全名);Console.WriteLine("Last Access: {0}",信息:上次访问时间);Console.WriteLine("长度:{ 0 },信息长度;Console.WriteLine("Attributes: {0}",info.Attributes);Console.WriteLine("将文件设为只读”;info.attributes=fileattributes.readonly;Console.WriteLine("Attributes: {0}",info.Attributes);Console.WriteLine("删除只读属性“;info.attributes&=~fileattributes.readonly;Console.WriteLine("Attributes: {0}",info.Attributes);;

你可以使用文件信息实例打开文件进行读写,移动文件,重命名文件,and also modifying the security settings on a file.Some of the functions provided by a文件信息instance duplicate those provided by theFile班级。TheFileclass is most useful when you want to perform an action on a single file.The文件信息当您想处理大量文件时,类是最有用的。在下一节中,您将了解如何获取文件信息目录中的项目并对其进行处理。

使用Directory和DirectoryInfo类

A file system can create files that contain collections of file information items.这些叫做目录orfolders.目录可以包含有关目录的目录信息,它允许用户创建树结构的目录。

As with files,使用目录有两种方法:号码簿类与DirectoryInfo班级。The号码簿班级就像File班级。它是一个静态类,提供可以枚举目录内容和创建和操作目录的方法。Listing 4-9 shows how a program can use the号码簿class to create a directory,证明它存在,然后删除它。请注意,如果程序试图删除一个非空目录,则会引发异常。

LISTING 4-9 The Directory class

目录。创建目录(“TestDir";if (Directory.Exists("TestDir"))    Console.WriteLine("目录创建成功“;目录。删除(“TestDir";Console.WriteLine("已成功删除目录“;;

的实例DirectoryInfoclass describes the contents of one directory.类还提供可用于创建和操作目录的方法。清单4-10使用DirectoryInfo班级。

清单4-10 directoryInfo类

directoryinfo localdir=新directoryinfo(“TestDir";localDir.Create();if(localDir.Exists)    Console.WriteLine("目录创建成功“;localdir.delete();Console.WriteLine("已成功删除目录“;;

Files and paths

路径定义文件在存储设备上的位置。In all the example programs above we have simply given the path as a string of text.在这种情况下,正在创建的文件或目录将与正在运行的程序位于同一目录中,并具有给定的名称。如果要在计算机上的不同位置存储文件,则需要创建更复杂的路径。

Paths can berelativeorabsolute.相对路径指定文件相对于当前运行程序的文件夹的位置。到目前为止,我们指定的所有路径都是相对于当前目录的。When expressing paths,性格““.“(句号)具有特殊意义。A single period"“.“means the current directory.双周期”“..“指当前目录上方的目录。You can use relative paths to specify a file in a parent directory,or a file in a directory in another part of the tree.接下来,您可以看到用于定位随此文本示例程序提供的图像目录的路径。The@character at the start of the string literal marks the string as a逐字逐句的字符串。This means that any escape characters in the string will be ignored.这很有用,因为否则字符串中的反斜杠字符可能被解释为转义字符。

string imagePath =  @"…\…\…\…\…\图像”;;

The program is running in the debug directory.The path must"climb"在四个父目录中查找图像目录。图中图4-4显示目录的结构。

图4-4

图4-4Navigating a relative path

绝对路径包括驱动器号,并标识文件路径中的所有子目录。这里的语句给出了文档的路径test.txt文件计算机上的文件夹。

字符串abspath=@“C:\users\rob\documents\test.txt““

The path to a file contains two elements: the directories in the path and the name of the file in the directory.ThePath类提供了许多非常有用的方法,可以用来处理程序中的路径。它提供了从完整路径中删除文件名的方法,更改文件名的扩展名,合并文件名和目录路径。清单4-11显示了Path可以使用。

清单4-11使用路径

字符串全名=@“C:\users\rob\documents\test.txt“;字符串dirname=path.getdirectoryname(全名);string filename=path.getfilename(全名);string fileextension=path.getextension(全名);string lisname=path.changeextension(全名,“.lis";string newTest = Path.Combine(dirName,“新测试;Console.WriteLine("全称:{ 0 },全名);Console.WriteLine("文件目录:0”,dirName);Console.WriteLine("文件名称:{0}”,文件名);Console.WriteLine("File extension: {0}",fileExtension);Console.WriteLine("文件扩展名:0_,;Console.WriteLine("新测试:{ 0 },newTest);;

当您运行清单4-11中的程序时,它会产生以下输出:

全名:C:\users\rob\documents\test.txtfile目录:C:\users\rob\documentsfile名称:test.txtfile扩展名:.txtfile with lis扩展名:C:\users\rob\documents\test.lisnew测试:C:\users\rob\documents\newtest.txt

ThePathclass is very useful and should总是优先用于手动处理路径字符串。ThePath类还提供可以生成临时文件名的方法。

Searching for files

TheDirectoryInfo类提供一个名为获取文件可以用来收集文件信息items that describe the files in a directory.一次过载获取文件可以接受搜索字符串。在搜索字符串中*可以表示任意数量的字符和字符??可以表示单个字符。

清单4-12中的程序使用获取文件列出示例程序中的所有C源文件。注意,这个程序还提供了使用递归的良好演示,其中findfiles方法调用自己来处理在给定目录中找到的任何目录。

清单4-12 C夏普程序

静态void findfiles(directoryinfo dir,string searchpattern)foreach(directoryinfo directory in dir.getdirectories())findfiles(directory,搜索模式;fileinfo[]matchingfiles=dir.getfiles(searchpattern);foreach(匹配文件中的fileinfo fileinfo)console.writeline(fileinfo.fullname);静态void main(string[]args)directoryinfo startdir=new directoryinfo(@)..\..\..\..";字符串searchString=“*CS;FindFiles(startDir,搜索字符串);Console.ReadKey();}

目录类提供了一个名为EnumerateFiles的方法,该方法也可用于以这种方式枚举文件。

通过使用System.NET命名空间中的类从网络读取和写入

NET框架提供了一系列可以与TCP/IP(传输控制协议/Internet协议)网络交互的应用程序编程接口。C程序可以创建网络socket通过发送未确认的消息,可以通过网络进行通信的对象数据报使用UDP(用户数据报协议)或使用TCP(传输控制协议)创建托管连接。

在本节中,我们将重点讨论System.NET命名空间中的类,这些类允许程序使用HTTP(超文本传输协议)与服务器通信。此协议在TCP/IP连接之上运行。换言之,TCP/IP提供服务器和客户机系统之间的连接,HTTP定义通过该连接交换的消息的格式。

一个HTTP客户端,例如Web浏览器,创建到服务器的TCP连接,并通过发送HTTP GET命令请求数据。The server will then respond with a page of information.响应返回到客户端后,TCP连接将关闭。

The information returned by the server is formatted using HTML (HyperText Markup Language) and rendered by the browser.对于ASP(Active Server Pages)应用程序(例如我们在第3章开头创建的应用程序),HTML文档可以由软件动态生成,而不是从存储在服务器上的文件加载。

HTTP最初用于共享人类可读的网页。However,now an HTTP request may return an XML or JSON formatted document that describes data in an application.

其余(代表性状态转移)架构使用GET,PUT,发布和删除HTTP操作,以允许客户机请求服务器在客户机-服务器应用程序中执行功能。用于与这些服务器和其他服务器通信的基本操作是发送“Web请求”到服务器以在服务器上执行HTML命令,现在我们将发现如何做到这一点。让我们看看与Web服务器交互的三种不同方式,并考虑它们的优缺点。这些是WebRequest,WebClient,和HTTP客户端。

WebRequest

WebRequest类是一个抽象基类,用于指定Web请求的行为。它公开一个名为create的静态工厂方法,给出了一个通用资源识别号(uri)指定要使用的资源的字符串。create方法检查给定的URI,并返回与该资源匹配的WebRequest类的子级。create方法可以创建httpwebrequest,FtpWebRequest,和FileWebRequest对象。如果是网站,the URI string will start with"“http”或““http:”and the Create method will return an HttpWebRequest instance.

httpWebRequest上的getResponse方法返回描述服务器响应的WebResponse实例。请注意,此响应不是网页本身,but an object that describes the response from the server.要从网页实际读取文本,程序必须在响应上使用getResponseStream方法来获取可从中读取网页文本的流。Listing 4-13 shows how this works.

清单4-13 httpwebrequest

webRequest webRequest=webRequest.create(“https://www.microsoft.com“;webResponse webResponse=webRequest.getResponse();using (StreamReader responseReader = new StreamReader(webResponse.GetResponseStream())){    string siteText = responseReader.ReadToEnd();console.writeline(站点文本);}

请注意,在streamreader周围使用可确保在读取网页响应时关闭输入流。It is important that either this stream or the WebResponse instance are explicitly closed after use,否则,连接将不会被重用,程序可能会耗尽Web连接。

使用WebRequest实例来读取网页,but it is rather complicated.It does,然而,其优点是程序可以在Web上设置广泛的属性,并请求根据特定的服务器需求对其进行定制。对于我们将要考虑的其他一些方法,这种灵活性是不可用的。

The code in Listing 4-13 is synchronous,in that the program will wait for the web page response to be generated and the response to be read.It is possible to use the WebRequest in an asynchronous manner so that a program is not paused in this way.However,当操作完成时,程序员必须创建要调用的事件处理程序。

网络客户端

The网络客户端类提供了一种从Web服务器读取文本的简单快捷的方法。清单4-14显示了如何实现这一点。现在不需要创建一个流来读取页面内容(尽管如果您愿意,也可以这样做),并且在从服务器获取答复之前,不需要处理对Web请求的响应。清单4-14显示了这是如何工作的。

LISTING 4-14 WebClient

webclient client=new webclient();string sitetext=client.downloadstring(“http://www.microsoft.com“;console.writeline(站点文本);;

The网络客户端类还提供可用于异步从服务器读取的方法。清单4-15用于Windows演示基础(WPF)应用程序,以读取在窗口中显示的网页内容。

LISTING 4-15 WebClient async

异步任务
          
           readwebpage(string uri)webclient client=new webclient();返回wait client.downloadStringTaskAsync(uri);}
          

HttpClient

TheHTTP客户端很重要,因为它是Windows通用应用程序下载网站内容的方式。Unlike theWebRequest以及网络客户端类,安HTTP客户端只提供异步的方法。It can be used in a very similar manner to the网络客户端,如清单4-16所示。

LISTING 4-16 HttpClient

异步任务
          
           readwebpage(string uri)httpclient client=new httpclient();返回wait client.getStringAsync(uri);}
          

Exception handling

和文件处理一样,从Internet加载信息容易出错。网络连接可能断开或服务器不可用。这意味着Web请求代码应该包含在适当的异常处理程序中。The code next is part of the program in Listing 4-16;它捕获异步加载方法引发的异常,并显示MeaseDealCalax包含错误信息。

try string webtext=等待readwebpage(pageuritextbox.text);ResultTextBlock.Text = webText;}catch (Exception ex){    var dialog = new MessageDialog(ex.Message,“请求失败;等待dialog.showAsync();}

实现异步I/O操作

到目前为止,示例程序中的所有文件输入/输出synchronous.调用方法执行文件操作的程序必须等待该方法完成,然后才能转到下一条语句。程序的用户必须等待文件操作完成,然后才能执行其他操作,这可能导致非常糟糕的用户体验。

In Chapter 1,技能中的“使用异步和等待,“您看到程序可以使用任务来执行方法的异步后台执行。It is worth taking a look at that section to refresh your understanding of these elements before reading further.

由提供的文件操作File类没有任何异步版本,so the文件流class should be used instead.清单4-17显示了一个函数,该函数使用异步写入将字节数组写入指定的文件。

清单4-17异步文件写入

异步任务writebytesasync(字符串文件名,byte [] items){    using (FileStream outStream = new FileStream(filename,FileMode.OpenOrCreate,fileaccess.write))等待outstream.writeasync(items,0,项目;长度);}

演示程序是一个包含同步和异步文件写入方法的Windows演示基础应用程序。图4-5shows the program display.用户可以根据选择的启动按钮,同步或异步地将大量值写入文件。They can also test the responsiveness of the application by selecting the获得时间按钮,显示当前时间。当编写器的同步版本正在运行时,他们应该注意到用户界面在短时间内没有响应。

图4-5

图4-5异步文件编写器演示

处理异步方法中的异常

If any exceptions are thrown by the asynchronous file write method they must be caught and a message displayed for the user.只有当写入字节同步方法返回任务写入字节同步方法被调用。清单4-18显示了一个正确执行此操作的按钮事件处理程序,并捕获可能由文件写入操作引发的异常。

LISTING 4-18 File exceptions

private async void starttask按钮\单击(对象发送器,routedeventargs e)byte[]data=new byte[100];try//请注意,文件名包含无效字符await writebytesasynctask(“demo:.dat",数据);}    catch (Exception writeException)    {        MessageBox.Show(writeException.Message,“文件写入失败“;}

当您运行清单4-18的示例程序时,该程序显示一个菜单,允许您在两种写入方法之间进行选择;one which returns a Task and the other of which is void.两次写入都将引发异常,but only the write that returns a Task will catch the exception correctly (see图4-6)并显示一个消息框。The other exception will not be handled correctly.

图4-6

图4-6Catching file exceptions

唯一应该返回的异步方法无效是Windows控件的实际事件处理程序。其他所有异步方法都必须返回结果或任务,以便正确处理该方法引发的任何异常。