前文:
- C#基础-1-标识符与数据类型
- C#基础-2-类型转换、变量与常量
- C#基础-3-运算符与判断
- C#基础-4-循环与封装
- C#基础-5-方法与可空类型
- C#基础-6-数组和Array类
- C#基础-7-字符串与String类
- C#基础-8-结构体与枚举
- C#基础-9-类与函数
- C#基础-10-继承与多态性
- C#基础-11-运算符重载与接口
- C#基础-12-命名空间与预处理指令
- C#基础-13-正则表达式与Regex类
异常处理
异常是在程序执行期间出现的问题。C# 中的异常是对程序运行时出现的特殊情况的一种响应,比如尝试除以零。
异常提供了一种把程序控制权从某个部分转移到另一个部分的方式。C# 异常处理时建立在四个关键词之上的:
- try:一个
try
块标识了一个将被激活的特定的异常的代码块。后跟一个或多个catch
块。 - catch:程序通过异常处理程序捕获异常。
catch
关键字表示异常的捕获。 - finally:
finally
块用于执行给定的语句,不管异常是否被抛出都会执行。例如,如果您打开一个文件,不管是否出现异常文件都要被关闭。 - throw:当问题出现时,程序抛出一个异常。使用
throw
关键字来完成。
语法
假设一个代码块将出现异常,用一个方法使用 try
和 catch
关键字捕获异常。try/catch
块内的代码为受保护的代码,示例:
try
{
// 引起异常的语句
}
catch( ExceptionName e1 )
{
// 错误处理代码
}
catch( ExceptionName e2 )
{
// 错误处理代码
}
catch( ExceptionName eN )
{
// 错误处理代码
}
finally
{
// 要执行的语句
}
异常类
C# 异常是使用类来表示的。
异常类主要是直接或间接地派生于 System.Exception
类。
System.ApplicationException
和 System.SystemException
类是派生于 System.Exception
类的异常类。
System.ApplicationException
类支持由应用程序生成的异常。所以程序员定义的异常都应派生自该类。
System.SystemException
类是所有预定义的系统异常的基类。
下表列出了一些派生自 System.SystemException
类的预定义的异常类:
异常类 | 描述 |
---|---|
System.IO.IOException |
处理 I/O 错误。 |
System.IndexOutOfRangeException |
处理当方法指向超出范围的数组索引时生成的错误。 |
System.ArrayTypeMismatchException |
处理当数组类型不匹配时生成的错误。 |
System.NullReferenceException |
处理当依从一个空对象时生成的错误。 |
System.DivideByZeroException |
处理当除以零时生成的错误。 |
System.InvalidCastException |
处理在类型转换期间生成的错误。 |
System.OutOfMemoryException |
处理空闲内存不足生成的错误。 |
System.StackOverflowException |
处理栈溢出生成的错误。 |
处理方法
C# 以 try
和 catch
块的形式提供了一种结构化的异常处理方案。使用这些块,把核心程序语句与错误处理语句分离开。
这些错误处理块是使用 try
、catch
和 finally
关键字实现的。下面是一个当除以零时抛出异常的实例:
using System;
namespace ErrorHandlingApplication
{
class DivNumbers
{
int result;
DivNumbers()
{
result = 0;
}
public void division(int num1, int num2)
{
try
{
result = num1 / num2;
}
catch (DivideByZeroException e)
{
Console.WriteLine($"Exception caught: {e}");
}
finally
{
Console.WriteLine($"Result: {result}");
}
}
static void Main(string[] args)
{
DivNumbers d = new DivNumbers();
d.division(25, 0);
Console.ReadKey();
}
}
}
输出:
自定义异常
用户自定义的异常类是派生自 ApplicationException
类,示例:
using System;
namespace UserDefinedException
{
class TestTemperature
{
static void Main(string[] args)
{
Temperature temp = new Temperature();
try
{
// 1 调用 Temperature 类的 ShowTemp() 函数
temp.ShowTemp();
}
// 5 捕获到异常,创建一个异常 TempIsZeroException 的实例 e
catch(TempIsZeroException e)
{
// 6 输出异常实例 e 的错误信息,Message 为异常的固定返回格式,或一个空的字符串
Console.WriteLine($"TempIsZeroException: {e.Message}");
}
// Console.ReadKey();
}
}
}
public class TempIsZeroException: ApplicationException
{
public TempIsZeroException(string message): base(message)
{
}
}
public class Temperature
{
// 2 故意设置 _temperature 为 0 触发异常
int _temperature = 0;
public void ShowTemp()
{
if(_temperature == 0)
{
// 3 如果 _temperature 为 0 则创建一个异常,传入要显示的信息给 message
throw (new TempIsZeroException("Zero Temperature found"));
}
else
{
// 4 如果 _temperature 不为 0 的时候直接输出其值
Console.WriteLine($"Temperature: {_temperature}");
}
}
}
输出:
抛出对象
如果异常是直接或间接派生自 System.Exception
类,可以抛出一个对象,可以在 catch
块中使用 throw
语句来抛出当前的对象,如下所示:
Catch(Exception e)
{
...
Throw e
}
文件输入输出
一个 文件 是一个存储在磁盘中带有 指定名称 和 目录路径 的 数据集合。
当打开文件进行读写时,它变成一个 流。
从根本上说,流是通过通信路径传递的字节序列。
有两个主要的流:
- 输入流: 输入流用于从文件读取数据(读操作)。
- 输出流: 输出流用于向文件写入数据(写操作)。
IO 类
System.IO
命名空间有各种不同的类,用于执行各种文件操作,如创建和删除文件、读取或写入文件,关闭文件等。
一些 System.IO
命名空间中常用的非抽象类:
类名 | 描述 |
---|---|
BinaryReader |
从二进制流读取原始数据。 |
BinaryWriter |
以二进制格式写入原始数据。 |
BufferedStream |
字节流的临时存储。 |
Directory |
有助于操作目录结构。 |
DirectoryInfo |
用于对目录执行操作。 |
DriveInfo |
提供驱动器的信息。 |
File |
有助于处理文件。 |
FileInfo |
用于对文件执行操作。 |
FileStream |
用于文件中任何位置的读写。 |
MemoryStream |
用于随机访问存储在内存中的数据流。 |
Path |
对路径信息执行操作。 |
StreamReader |
用于从字节流中读取字符。 |
StreamWriter |
用于向一个流中写入字符。 |
StringReader |
用于读取字符串缓冲区。 |
StringWriter |
用于写入字符串缓冲区。 |
FileStream类
System.IO
命名空间中的 FileStream
类有助于文件的读写与关闭。
该类派生自抽象类 Stream
。
创建一个 FileStream
对象来创建一个新的文件,或打开一个已有的文件,语法如下:
FileStream <object_name> = new FileStream( <file_name>,
<FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>);
例如,创建一个 FileStream
对象 F
来读取名为 sample.txt
的文件:
FileStream F = new FileStream("sample.txt", FileMode.Open, FileAccess.Read, FileShare.Read);
FileMode
枚举定义了各种打开文件的方法,其成员有:
Append
:打开一个已有的文件,并将光标放置在文件的末尾。如果文件不存在,则创建文件。Create
:创建一个新的文件。如果文件已存在,则删除旧文件,然后创建新文件。CreateNew
:指定操作系统应创建一个新的文件。如果文件已存在,则抛出异常。Open
:打开一个已有的文件。如果文件不存在,则抛出异常。OpenOrCreate
:指定操作系统应打开一个已有的文件。如果文件不存在,则用指定的名称创建一个新的文件打开。Truncate
:打开一个已有的文件,文件一旦打开,就将被截断为零字节大小。然后我们可以向文件写入全新的数据,但是保留文件的初始创建日期。如果文件不存在,则抛出异常。
FileAccess
枚举的成员有:Read
、ReadWrite
和 Write
。
FileShare
枚举的成员有:
Inheritable
:允许文件句柄可由子进程继承。Win32 不直接支持此功能。None
:谢绝共享当前文件。文件关闭前,打开该文件的任何请求(由此进程或另一进程发出的请求)都将失败。Read
:允许随后打开文件读取。如果未指定此标志,则文件关闭前,任何打开该文件以进行读取的请求(由此进程或另一进程发出的请求)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。ReadWrite
:允许随后打开文件读取或写入。如果未指定此标志,则文件关闭前,任何打开该文件以进行读取或写入的请求(由此进程或另一进程发出)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。Write
:允许随后打开文件写入。如果未指定此标志,则文件关闭前,任何打开该文件以进行写入的请求(由此进程或另一进过程发出的请求)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。Delete
:允许随后删除文件。
实例:
using System;
using System.IO;
namespace FileIOApplication
{
class Program
{
static void Main(string[] args)
{
// 如果 test.dat 存在,则打开它;如果不存在,则创建新文件。
FileStream F = new FileStream("test.dat",
FileMode.OpenOrCreate, FileAccess.ReadWrite);
// 往文件 test.dat 里写入 1-20 的字节
for (int i = 1; i <= 20; i++)
{
F.WriteByte((byte)i);
}
// 重置文件指针到文件开头 0
F.Position = 0;
// 读取并输出文件中的内容
for (int i = 0; i <= 20; i++)
{
Console.Write(F.ReadByte() + " ");
}
// 关闭文件
F.Close();
Console.ReadKey();
}
}
}
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -1
最后一个 -1 是因读取超出文件长度导致的默认返回值。
文本文件读写
StreamReader类
StreamReader
类继承自抽象基类 TextReader
,表示阅读器读取一系列字符。
下面列出了 StreamReader
类中三个常用的方法:
public override void Close()
:关闭 StreamReader 对象和基础流,并释放任何与读者相关的系统资源。public override int Peek()
:返回下一个可用的字符,但不使用它。public override int Read()
:从输入流中读取下一个字符,并把字符位置往前移一个字符。
示例 C 盘根目录下有一个 Jamaica.txt
文件:
Down the way where the nights are gay
And the sun shines daily on the mountain top
I took a trip on a sailing ship
And when I reached Jamaica
I made a stop
读取该文件的内容:
using System;
using System.IO;
namespace FileApplication
{
class Program
{
static void Main(string[] args)
{
try
{
// 创建一个 StreamReader 的实例来读取文件
// using 语句也能关闭 StreamReader
using (StreamReader sr = new StreamReader("C:/Jamaica.txt", Encoding.UTF8))
{
string line;
// 从文件读取并显示行,直到文件的末尾
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
catch (Exception e)
{
// 向用户显示出错消息
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
Console.ReadKey();
}
}
}
使用
using
语句自动管理资源,退出using
块时自动关闭文件流。
运行后输出文件内容。
StreamWriter类
StreamWriter
类继承自抽象类 TextWriter
,表示编写器写入一系列字符。
下面列出一些常用的方法:
public override void Close()
:关闭当前的 StreamWriter 对象和基础流。public override void Flush()
:清理当前编写器的所有缓冲区,使得所有缓冲数据写入基础流。public virtual void Write(bool value)
:把一个布尔值的文本表示形式写入到文本字符串或流(继承自 TextWriter)。public override void Write( char value )
:把一个字符写入到流。public virtual void Write( decimal value )
:把一个十进制值的文本表示形式写入到文本字符串或流。public virtual void Write( double value )
:把一个 8 字节浮点值的文本表示形式写入到文本字符串或流。public virtual void Write( int value )
:把一个 4 字节有符号整数的文本表示形式写入到文本字符串或流。public override void Write( string value )
:把一个字符串写入到流。public virtual void WriteLine()
:把行结束符写入到文本字符串或流。
示例:
using System;
using System.IO;
namespace FileApplication
{
class Program
{
static void Main(string[] args)
{
string[] names = new string[] {"Zara Ali", "Nuha Ali"};
using (StreamWriter sw = new StreamWriter("names.txt"))
{
foreach (string s in names)
{
sw.WriteLine(s);
}
}
// 从文件中读取并显示每行
string line = "";
using (StreamReader sr = new StreamReader("names.txt"))
{
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
Console.ReadKey();
}
}
}
输出:
Zara Ali
Nuha Ali
二进制文件读写
BinaryReader类
BinaryReader
类用于从文件读取二进制数据。
一个 BinaryReader
对象通过向它的构造函数传递 FileStream
对象而被创建。
常用方法:
public override void Close()
:关闭 BinaryReader 对象和基础流。public virtual int Read()
:从基础流中读取字符,并把流的当前位置往前移。public virtual bool ReadBoolean()
:从当前流中读取一个布尔值,并把流的当前位置往前移一个字节。public virtual byte ReadByte()
:从当前流中读取下一个字节,并把流的当前位置往前移一个字节。public virtual byte[] ReadBytes( int count )
:从当前流中读取指定数目的字节到一个字节数组中,并把流的当前位置往前移指定数目的字节。public virtual char ReadChar()
:从当前流中读取下一个字节,并把流的当前位置按照所使用的编码和从流中读取的指定的字符往前移。
BinaryWriter类
BinaryWriter
类用于向文件写入二进制数据。
一个 BinaryWriter
对象通过向它的构造函数传递 FileStream
对象而被创建。
常用方法:
public override void Close()
:关闭 BinaryWriter 对象和基础流。public virtual void Flush()
:清理当前编写器的所有缓冲区,使得所有缓冲数据写入基础设备。public virtual long Seek( int offset, SeekOrigin origin )
:设置当前流内的位置。public virtual void Write( bool value )
:把一个单字节的布尔值写入到当前流中,0 表示 false,1 表示 true。public virtual void Write( byte value )
:把一个无符号字节写入到当前流中,并把流的位置往前移一个字节。public virtual void Write( byte[] buffer )
:把一个字节数组写入到基础流中。
实例:
using System;
using System.IO;
namespace BinaryFileApplication
{
class Program
{
static void Main(string[] args)
{
BinaryWriter bw;
BinaryReader br;
int i = 25;
double d = 3.14157;
bool b = true;
string s = "I am happy";
// 创建文件
try
{
bw = new BinaryWriter(new FileStream("mydata",
FileMode.Create));
}
catch (IOException e)
{
Console.WriteLine(e.Message + "\n Cannot create file.");
return;
}
// 写入文件
try
{
bw.Write(i);
bw.Write(d);
bw.Write(b);
bw.Write(s);
}
catch (IOException e)
{
Console.WriteLine(e.Message + "\n Cannot write to file.");
return;
}
bw.Close();
// 读取文件
try
{
br = new BinaryReader(new FileStream("mydata",
FileMode.Open));
}
catch (IOException e)
{
Console.WriteLine(e.Message + "\n Cannot open file.");
return;
}
try
{
i = br.ReadInt32();
Console.WriteLine($"Integer data: {i}");
d = br.ReadDouble();
Console.WriteLine($"Double data: {d}");
b = br.ReadBoolean();
Console.WriteLine($"Boolean data: {b}");
s = br.ReadString();
Console.WriteLine($"String data: {s}");
}
catch (IOException e)
{
Console.WriteLine(e.Message + "\n Cannot read from file.");
return;
}
br.Close();
Console.ReadKey();
}
}
}
输出:
Integer data: 25
Double data: 3.14157
Boolean data: True
String data: I am happy
Windows系统文件操作
DirectoryInfo类
DirectoryInfo
类派生自 FileSystemInfo
类。
它提供了各种用于创建、移动、浏览目录和子目录的方法。该类不能被继承。
常用属性:
Attributes
:获取当前文件或目录的属性。CreationTime
:获取当前文件或目录的创建时间。Exists
:获取一个表示目录是否存在的布尔值。Extension
:获取表示文件存在的字符串。FullName
:获取目录或文件的完整路径。LastAccessTime
:获取当前文件或目录最后被访问的时间。Name
:获取该 DirectoryInfo 实例的名称。
常用方法:
public void Create()
:创建一个目录。public DirectoryInfo CreateSubdirectory( string path )
:在指定的路径上创建子目录。指定的路径可以是相对于 DirectoryInfo 类的实例的路径。public override void Delete()
:如果为空的,则删除该 DirectoryInfo。public DirectoryInfo[] GetDirectories()
:返回当前目录的子目录。public FileInfo[] GetFiles()
:从当前目录返回文件列表。
FileInfo类
FileInfo
类派生自 FileSystemInfo
类。
它提供了用于创建、复制、删除、移动、打开文件的属性和方法,且有助于 FileStream
对象的创建。该类不能被继承。
常用属性:
Attributes
:获取当前文件的属性。CreationTime
:获取当前文件的创建时间。Directory
:获取文件所属目录的一个实例。Exists
:获取一个表示文件是否存在的布尔值。Extension
:获取表示文件存在的字符串。FullName
:获取文件的完整路径。LastAccessTime
:获取当前文件最后被访问的时间。LastWriteTime
:获取文件最后被写入的时间。Length
:获取当前文件的大小,以字节为单位。Name
:获取文件的名称。
常用方法:
public StreamWriter AppendText()
:创建一个 StreamWriter,追加文本到由 FileInfo 的实例表示的文件中。public FileStream Create()
:创建一个文件。public override void Delete()
:永久删除一个文件。public void MoveTo( string destFileName )
:移动一个指定的文件到一个新的位置,提供选项来指定新的文件名。public FileStream Open( FileMode mode )
:以指定的模式打开一个文件。public FileStream Open( FileMode mode, FileAccess access )
:以指定的模式,使用 read、write 或 read/write 访问,来打开一个文件。public FileStream OpenRead()
:创建一个只读的 FileStream。public FileStream OpenWrite()
:创建一个只写的 FileStream。
输出 C 盘下 Windows 文件夹内文件列表实例:
using System;
using System.IO;
namespace WindowsFileApplication
{
class Program
{
static void Main(string[] args)
{
// 创建一个 DirectoryInfo 对象
DirectoryInfo mydir = new DirectoryInfo(@"C:\Windows");
// 获取目录中的文件以及它们的名称和大小
FileInfo [] f = mydir.GetFiles();
foreach (FileInfo file in f)
{
Console.WriteLine("File Name: {0} Size: {1}",
file.Name, file.Length);
}
Console.ReadKey();
}
}
}