C#理论 —— 文件操作、委托和事件

文章目录

  • 1. 文件操作
    • 1.1 获取计算机驱动器信息Driveinfo
    • 1.2 文件夹操作Directory、Directoryinfo
    • 1.3 文件操作File、FileInfo
    • 1.4 文件路径操作Path
    • 1.5 流——文件的输入输出
      • 1.5.1 I/O 类
      • 1.5.2 FileStream 类
      • 1.5.3 StreamReader类:读取文件
      • 1.5.4 StreamWriter类:写入文件
      • 1.5.5 BinaryReader类:读取二进制文件
      • 1.5.6 BinaryWriter类:写入二进制数据
      • 1.5.7 文本文件的读写
      • 1.5.8 二进制文件的读写
      • 1.5.9 Windows 文件系统的操作
  • 2. 委托和事件
    • 2.1 委托Delegate
      • 2.1.1 命名方法委托
      • 2.1.2 多播委托
      • 2.1.3 匿名委托
    • 2.2 事件Event
  • 3. Excel 操作
    • 3.1 添加命名空间引用
    • 3.2 excel 应用的创建与销毁
    • 3.3 打开Excel
    • 3.4 读取Excel
  • *. 参考:

1. 文件操作


操作变量和常量时这些值都是存放到内存中的,当程序运行结束后使用的数据全部被删除。若需要长久保存应用程序中的数据,需要选用文件或数据库来存储。

文件操作类在 System.IO 命名空间中,包括 Driveinfo 类、Directory 类、Directoryinfo 类、File 类、Filelnfo 类、Path 类等。

1.1 获取计算机驱动器信息Driveinfo

查看计算机驱动器信息主要包括查看磁盘的空间、磁盘的文件格式、磁盘的卷标等,在 C# 语言中这些操作可以通过 Driveinfo 类来实现。

Driveinfo 类是一个密封类,即不能被继承,其仅提供了一个构造方法,语法形式如下。

Driveinfo(string driveName) // dirveName 参数是指有效驱动器路径或驱动器号

Driveinfo driveInfo=new Driveinfo("C"); // 获取C盘信息

Driveinfo 类中的常用属和方法如下表所示:

属性或方法 作用
AvailableFreeSpace 只读属性,获取驱动器上的可用空闲空间量 (以字节为单位)
DriveFormat 只读属性,获取文件系统格式的名称,例如 NTFS 或 FAT32
DriveType 只读属性,获取驱动器的类型,例如 CD-ROM、可移动驱动器、网络驱动器或固定驱动器
IsReady 只读属性,获取一个指示驱动器是否已准备好的值,True 为准备好了, False 为未准备好
Name 只读属性,获取驱动器的名称,例如 C:\
RootDirectory 只读属性,获取驱动器的根目录
TotalFreeSpace 只读属性,获取驱动器上的可用空闲空间总量 (以字节为单位)
TotalSize 只读属性,获取驱动器上存储空间的总大小 (以字节为单位)
VolumeLabel 属性, 获取或设置驱动器的卷标
Driveinfo[] GetDrives() 静态方法,检索计算机上所有逻辑驱动器的驱动器名称
  • 实例:
using System;
using System.IO;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            // 获取 D 盘中的驱动器信息
            DriveInfo driveInfo = new DriveInfo("D");
            Console.WriteLine("驱动器的名称:" + driveInfo.Name);
            Console.WriteLine("驱动器的卷标:" + driveInfo.VolumeLabel);
            Console.WriteLine("驱动器类型:" + driveInfo.DriveType);  
            Console.WriteLine("驱动器的文件格式:" + driveInfo.DriveFormat);
            Console.WriteLine("驱动器的根目录:" + driveInfo.RootDirectory);
            Console.WriteLine("驱动器中可用空间大小:" + driveInfo.TotalFreeSpace + " byte");
            Console.WriteLine("驱动器中可用空闲空间大小:" + driveInfo.AvailableFreeSpace + " byte");
            Console.WriteLine("驱动器总大小:" + driveInfo.TotalSize + " byte");
            Console.WriteLine("驱动器是否已准备好:" + driveInfo.IsReady);
            Console.ReadKey();

            Console.WriteLine("-----------------------------------------------------------------------");

            // 获取计算机中所有驱动器的名称和文件格式
            DriveInfo[] driveInfo1 = DriveInfo.GetDrives();
            foreach (DriveInfo d in driveInfo1)
            {
                if (d.IsReady)
                {
                    Console.WriteLine("驱动器名称:" + d.Name);
                    Console.WriteLine("驱动器的文件格式" + d.DriveFormat);
                }
            }
            Console.ReadKey();

        }
    }
}
运行结果:
驱动器的名称:D:\
驱动器的卷标:本地磁盘
驱动器类型:Fixed
驱动器的文件格式:NTFS
驱动器的根目录:D:\
驱动器中可用空间大小:210795884544 byte
驱动器中可用空闲空间大小:210795884544 byte
驱动器总大小:333401026560 byte
驱动器是否已准备好:True
-----------------------------------------------------------------------
驱动器名称:C:\
驱动器的文件格式NTFS
驱动器名称:D:\
驱动器的文件格式NTFS
驱动器名称:E:\
驱动器的文件格式NTFS
驱动器名称:F:\
驱动器的文件格式NTFS

1.2 文件夹操作Directory、Directoryinfo

Directory 类和 Directoryinfo 类都是对文件夹进行操作。DirectoryInfo 类能创建该类的实例,通过类的实例访问类成员。DirectoryInfo 类提供了一个构造方法,语法形式如下:

DirectoryInfo(string path) // path 参数用于指定文件的目录

DirectoryInfo directoryInfo = new DirectoryInfo("D:\\test"); // 访问D盘中的test文件夹

注:路径中如果使用 \,要使用转义字符来表示,即 \\;或者在路径中将 \ 字符换成 /

DirectoryInfo 类中常用的属性和方法如下表所示:

属性或方法 作用
Exists 只读属性,获取指示目录是否存在的值
Name 只读属性,获取 Directorylnfo 实例的目录名称
Parent 只读属性,获取指定的子目录的父目录
Root 只读属性,获取目录的根部分
void Create() 创建目录
DirectoryInfo CreateSubdirectory(string path) 在指定路径上创建一个或多个子目录
void Delete() 如果目录中为空,则将目录删除
void Delete(bool recursive) 删除目录下的子目录和文件,如果 recursive 参数的值为 True,则直接删除。为false则先判断该目录是否为空目录,若是则删除,否则不删除并抛出“文件夹不为空”的异常
IEnumerable EnumerateDirectories() 返回当前目录中目录信息的可枚举集合
IEnumerable EnumerateDirectories(string searchPattern) 返回与指定的搜索模式匹配的目录信息的可枚举集合
IEnumerable EnumerateFiles() 返回当前目录中的文件信息的可枚举集合
IEnumerable EnumerateFiles(string searchPattern) 返回与搜索模式匹配的文件信息的可枚举集合
IEnumerable EnumerateFileSystemInfos() 返回当前目录中的文件系统信息的可枚举集合
IEnumerable EnumerateFileSystemInfos(string searchPattern) 返回与指定的搜索模式匹配的文件系统信息的可枚举集合
DirectoryInfo[] GetDirectories() 返回当前目录的子目录
DirectoryInfo[] GetDirectories(string searchPattern) 返回匹配给定的搜索条件的当前目录
FileInfo[] GetFiles() 返回当前目录的文件列表
FileInfo[] GetFiles(string searchPattern) 返回当前目录中与给定的搜索模式匹配的文件列表
FileSystemInfo[] GetFileSystemInfos() 返回所有文件和目录的子目录中的项
FileSystemInfo[] GetFileSystemInfos(string searchPattern) 返回与指定的搜索条件匹配的文件和目录的子目录中的项
void MoveTo(string destDirName) 移动 DirectoryInfo 实例中的目录到新的路径
using System;
using System.IO;
using System.Collections.Generic;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            DirectoryInfo directoryInfo = new DirectoryInfo("E:\\C#Code");
            directoryInfo.Create(); // 若路径文件夹不存在,则创建它
            directoryInfo.CreateSubdirectory("code-1"); // 创建名为code-1的子文件
            directoryInfo.CreateSubdirectory("code-2");

            // 读取指定文件夹内的文件夹
            IEnumerable<DirectoryInfo> dir = directoryInfo.EnumerateDirectories();
            foreach (var v in dir)
            {
                Console.WriteLine(v.Name);
            }
            Console.WriteLine("按任意键删除上述文件夹");
            Console.ReadKey();
            directoryInfo.Delete(true); // 删除整个文件夹
        }
    }
}

而Directory 类是一个静态类, 不能创建该类的实例,直接通过“类名 . 类成员”的形式调用其属性和方法。

  • 【实例】使用 Directory 类在 D 盘上操作 code 文件夹,要求先判断是否存在该文件夹,如果存在则删除,否则创建该文件夹。
using System;
using System.IO;
using System.Collections.Generic;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            string documentPath = "E:\\C#_code";
            bool flag = Directory.Exists(documentPath); // 判断文件夹是否存在
            if (flag)
            {
                Directory.Delete(documentPath, true);
            }
            else
            {
                Directory.CreateDirectory(documentPath);
            }
        }
    }
}

1.3 文件操作File、FileInfo

File 类和 FileInfo 类都是用来操作文件的,并且作用相似,它们都能完成对文件的创建、更改文件的名称、删除文件、移动文件等操作。

File 类是静态类,其成员也是静态的,通过类名即可访问类的成员;FileInfo 类不是静态成员,其类的成员需要类的实例来访问。

在 FileInfo 类中提供了一个构造方法,语法形式如下:

FileInfo(string fileName) // fileName 参数用于指定新文件的完全限定名或相对文件名

FileInfo 类中常用的属性和方法如下表所示:

属性或方法 作用
Directory 只读属性,获取父目录的实例
DirectoryName 只读属性,获取表示目录的完整路径的字符串
Exists 只读属性,获取指定的文件是否存在,若存在返回 True,否则返回 False
IsReadOnly 属性,获取或设置指定的文件是否为只读的
Length 只读属性,获取文件的大小
Name 只读属性,获取文件的名称
Filelnfo CopyTo(string destFileName) 将现有文件复制到新文件,不允许覆盖现有文件
Filelnfo CopyTo(string destFileName, bool overwrite) 将现有文件复制到新文件,允许覆盖现有文件
FileStream Create() 创建文件
void Delete() 删除文件
void MoveTo(string destFileName) 将指定文件移到新位置,提供要指定新文件名的选项
Filelnfo Replace(string destinationFileName, string destinationBackupFileName) 使用当前文件对象替换指定文件的内容,先删除原始文件, 再创建被替换文件的备份
  • 【实例】在 E 盘的 C#_code 文件夹下创建名为 test1.txt 的文件,并获取该文件的相关属性,然后将其移动到 E 盘下的 C#_code1 文件夹中。
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            string directoryPath1 = "E:\\C#_code";
            string directoryPath2 = "E:\\C#_code1";
            string filePath1 = "E:\\C#_code\\test1.txt";
            string filePath2 = "E:\\C#_code1\\test1.txt";

            //在E盘下创建C#_code文件夹
            Directory.CreateDirectory(directoryPath1);
            FileInfo fileInfo = new FileInfo(filePath1);
            DirectoryInfo directoryInfo1 = new DirectoryInfo(directoryPath1);
            DirectoryInfo directoryInfo2 = new DirectoryInfo(directoryPath2);
            IEnumerable<DirectoryInfo> dir1 = directoryInfo1.EnumerateDirectories();
            

            if (!fileInfo.Exists)
            {
                //创建文件
                fileInfo.Create().Close(); 
            }
            fileInfo.Attributes = FileAttributes.Normal;//设置文件属性
            //读取文件属性
            Thread.Sleep(1000); //延时2000毫秒
            Console.WriteLine("文件路径:" + fileInfo.Directory);
            Thread.Sleep(1000);
            Console.WriteLine("文件名称:" + fileInfo.Name);
            Thread.Sleep(1000);
            Console.WriteLine("文件是否只读:" + fileInfo.IsReadOnly);
            Thread.Sleep(1000);
            Console.WriteLine("文件大小:" + fileInfo.Length);
            Console.WriteLine("------------------------------------------------------");
            Console.WriteLine("输入任意按键以完成文件的转移...");
            Console.ReadKey();

            //先创建code-1 文件夹
            //再将文件移动到code_1文件夹下
            Directory.CreateDirectory(directoryPath2);
            IEnumerable<DirectoryInfo> dir2 = directoryInfo2.EnumerateDirectories();
            //判断目标文件夹中是否含有文件test1.txt
            FileInfo newFileInfo = new FileInfo(filePath2);
            if (!newFileInfo.Exists)
            {
                //移动文件到指定路径
                Console.WriteLine("文件转移中...");
                fileInfo.MoveTo(filePath2);
                Console.WriteLine("文件转移完成.");

            }
            Console.WriteLine("输入任意按键以完成文件夹的删除...");
            Console.ReadKey();
            directoryInfo1.Delete(true); // 删除整个文件夹
            directoryInfo2.Delete(true); // 删除整个文件夹
            Thread.Sleep(500);
            Console.WriteLine("文件夹删除完成.");
            Console.ReadKey();
        }
    }
}


File 类同样可以完成与 FileInfo 类相似的功能,但 File 类中也提供了一些不同的方法。

File 类中获取或设置文件信息的常用方法如下表所示:

属性或方法 作用
DateTime GetCreationTime(string path) 返回指定文件或目录的创建日期和时间
DateTime GetLastAccessTime(string path) 返回上次访问指定文件或目录的日期和时间
DateTime GetLastWriteTime(string path) 返回上次写入指定文件或目录的日期和时间
void SetCreationTime(string path, DateTime creationTime) 设置创建该文件的日期和时间
void SetLastAccessTime(string path, DateTime lastAccessTime) 设置上次访问指定文件的日期和时间
void SetLastWriteTime(string path, DateTime lastWriteTime) 设置上次写入指定文件的日期和时间
  • 【实例】将上述FileInfo实例中实现的内容使用 File 类完成。
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //在D盘下创建code文件夹
            Directory.CreateDirectory("E:\\code");
            Directory.CreateDirectory("E:\\code-1");
            string path = "E:\\code\\test1.txt";
            //创建文件
            FileStream fs = File.Create(path);
            //获取文件信息
            Console.WriteLine("文件创建时间:" + File.GetCreationTime(path));
            Console.WriteLine("文件最后被写入时间:" + File.GetLastWriteTime(path));
            //关闭文件流
            fs.Close();

            //设置目标路径
            string newPath = "E:\\code-1\\test1.txt";
            //判断目标文件是否存在
            bool flag = File.Exists(newPath);
            if (flag)
            {
                //删除文件
                File.Delete(newPath);
            }
            FileStream fs1 = File.Create(newPath);
            fs1.Close();
            DirectoryInfo directoryInfo1 = new DirectoryInfo("E:\\code");
            DirectoryInfo directoryInfo2 = new DirectoryInfo("E:\\code-1");
            IEnumerable<DirectoryInfo> dir1 = directoryInfo1.EnumerateDirectories();
            IEnumerable<DirectoryInfo> dir2 = directoryInfo2.EnumerateDirectories();
            directoryInfo1.Delete(true); // 删除整个文件夹
            directoryInfo2.Delete(true); // 删除整个文件夹

        }
    }
}

1.4 文件路径操作Path

Path 类主要用于文件路径的一些操作,它也是一个静态类。

ath 类中常用的属性和方法如下表所示:

属性或方法 作用
string ChangeExtension(string path, string extension) 更改路径字符串的扩展名
string Combine(params string[] paths) 将字符串数组组合成一个路径
string Combine(string path1, string path2) 将两个字符串组合成一个路径
string GetDirectoryName(string path) 返回指定路径字符串的目录信息
string GetExtension(string path) 返回指定路径字符串的扩展名
string GetFileName(string path) 返回指定路径字符串的文件名和扩展名
string GetFileNameWithoutExtension(string path) 返回不具有扩展名的指定路径字符串的文件名
string GetFullPath(string path) 返回指定路径字符串的绝对路径
char[] GetInvalidFileNameChars() 获取包含不允许在文件名中使用的字符的数组
char[] GetInvalidPathChars() 获取包含不允许在路径名中使用的字符的数组
string GetPathRoot(string path) 获取指定路径的根目录信息
string GetRandomFileName() 返回随机文件夹名或文件名
string GetTempPath() 返回当前用户的临时文件夹的路径
bool HasExtension(string path) 返回路径是否包含文件的扩展名
bool IsPathRooted(string path) 返回路径字符串是否包含根
  • 【实例】从控制台输入一个路径,输出该路径的不含扩展名的路径、扩展名、文件全 名、文件路径、更改文件扩展名。
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("请输入一个文件路径:");
            string path = Console.ReadLine();
            Console.WriteLine("不包含扩展名的文件名:" + Path.GetFileNameWithoutExtension(path));
            Console.WriteLine("文件扩展名:" + Path.GetExtension(path));
            Console.WriteLine("文件全名:" + Path.GetFileName(path));
            Console.WriteLine("文件路径:" + Path.GetDirectoryName(path));
            //更改文件扩展名
            string newPath = Path.ChangeExtension(path, "c");
            Console.WriteLine("更改后的文件全名:" + Path.GetFileName(newPath));
            Console.ReadKey();
        }
    }
}

1.5 流——文件的输入输出

一个文件是一个存储在磁盘中带有指定名称和目录路径的数据集合。对象间进行信息或者数据的交换时总是先将对象或数据转换为某种形式的流,再通过流的传输,到达目的对象后再将流转换为对象数据。所以, 可以把流看作是一种数据的载体,通过它可以实现数据交换和传输。

流是通过通信路径传递的字节序列。相对于某一对象,通常我们把对象接收外界的信息输入(Input)称为输入流,相应地从对象向外 输出(Output)信息为输出流,合称为输入/输出流(I/O Streams)。

流所在的命名空间是System.IO,主要包括文本文件的读写、图像和声音文件的读写、二进制文件的读写等。在 System.IO 命名空间中提供了多种类,用于进行文件和数据流的读写操作。

除了和磁盘文件直接相关的文件流以外,流还有多种类型。例如数据流 (Stream) 是对串行传输数据的一种抽象表示,是对输入/输出的一种抽象。

数据有来源和目的地,衔接两者的就是串流对象。用比喻的方式来说或,数据就好比水,串流对象就好比水管,通过水管的衔接,水由一端流向另一端。

例如将数据从来源取出,可以试用输入 ( 读 ) 串流,把数据储存在内存缓冲区;如果将数据写入目的地,可以使用输出 ( 写 ) 串流,把内存缓冲区的数据写入目的地。

典型的数据流和某个外部数据源相关,数据源可以是文件、外部设备、内存、网络套接字等。

根据数据源的不同,.Net 提供了多个从 Stream 类派生的子类,每个类代表一种具体的数据流类型,比如磁盘文件直接相关的文件流类 FileStream,和套接字相关的网络流类 NetworkStream,和内存相关的内存流类 MemoryStream 等。

1.5.1 I/O 类

System.IO 命名空间有各种不同的类,用于执行各种文件操作,如创建和删除文件、读取或写入文件,关闭文件等。

I/O 类 描述
BinaryReader 从二进制流读取原始数据。
BinaryWriter 以二进制格式写入原始数据。
BufferedStream 字节流的临时存储。
Directory 有助于操作目录结构。
DirectoryInfo 用于对目录执行操作。
DriveInfo 提供驱动器的信息。
File 有助于处理文件。
FileInfo 用于对文件执行操作。
FileStream 用于文件中任何位置的读写。
MemoryStream 用于随机访问存储在内存中的数据流。
Path 对路径信息执行操作。
StreamReader 用于从字节流中读取字符。
StreamWriter 用于向一个流中写入字符。
StringReader 用于读取字符串缓冲区。
StringWriter 用于写入字符串缓冲区。

1.5.2 FileStream 类

System.IO 命名空间中的 FileStream 类有助于文件的读写与关闭。它不仅能读写普通的文本文件,还可以读取图像文件、声音文件等不同格式的文件。

如需要创建一个 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 枚举定义了各种打开文件的方法。FileMode 枚举的成员有:
    • Append:打开一个已有的文件,并将光标放置在文件的末尾。如果文件不存在,则创建文件。
    • Create:创建一个新的文件。如果文件已存在,则删除旧文件,然后创建新文件。
    • CreateNew:指定操作系统应创建一个新的文件。如果文件已存在,则抛出异常。
    • Open:打开一个已有的文件。如果文件不存在,则抛出异常。
    • OpenOrCreate:指定操作系统应打开一个已有的文件。如果文件不存在,则用指定的名称创建一个新的文件打开。
    • Truncate:打开一个已有的文件,文件一旦打开,就将被截断为零字节大小。然后我们可以向文件写入全新的数据,但是保留文件的初始创建日期。如果文件不存在,则抛出异常。
  • FileAccess 枚举的成员有:Read、ReadWrite 和 Write。分别表示以只读方式、写方式、读写方式打开文件。
  • FileShare 枚举的成员有:
    • Inheritable:允许文件句柄可由子进程继承。Win32 不直接支持此功能。
    • None:谢绝共享当前文件。文件关闭前,打开该文件的任何请求(由此进程或另一进程发出的请求)都将失败。
    • Read:允许随后打开文件读取。如果未指定此标志,则文件关闭前,任何打开该文件以进行读取的请求(由此进程或另一进程发出的请求)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
    • ReadWrite:允许随后打开文件读取或写入。如果未指定此标志,则文件关闭前,任何打开该文件以进行读取或写入的请求(由此进程或另一进程发出)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
    • Write:允许随后打开文件写入。如果未指定此标志,则文件关闭前,任何打开该文件以进行写入的请求(由此进程或另一进过程发出的请求)都将失败。但是,即使指定了此标志,仍可能需要附加权限才能够访问该文件。
    • Delete:允许随后删除文件。
  • FileOptions 枚举类型用于设置文件的高级选项,包括文件是否加密、访问后是否删除等,具体的枚举值如下:
    • WriteThrough:指示系统应通过任何中间缓存、直接写入磁盘。
    • None:指示在生成 System.IO.FileStream 对象时不应使用其他选项。
    • Encrypted:指示文件是加密的,只能通过用于加密的同一用户账户来解密。
    • DeleteOnClose:指示当不再使用某个文件时自动删除该文件。
    • SequentialScan:指示按从头到尾的顺序访问文件。
    • RandomAccess:指示随机访问文件。
    • Asynchronous:指示文件可用于异步读取和写入。

FileStream 类的构造方法有很多,这里介绍一些常用的构造方法,如下表所示:

构造方法 说明
FileStream(string path, FileMode mode) 使用指定路径的文件、文件模式创建 FileStream 类的实例
FileStream(string path, FileMode mode, FileAccess access) 使用指定路径的文件、文件打开模式、文件访问模式创建 FileStream 类的实例
FileStream(string path, FileMode mode, FileAccess access, FileShare share) 使用指定的路径、创建模式、读写权限和共享权限创建 FileStream 类的一个新实例
FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options) 使用指定的路径、创建模式、读写权限和共享权限、其他 文件选项创建 FileStream 类的实例

FileStream 类中常用的属性和方法如下图所示:

属性或方法 作用
bool CanRead 只读属性,获取一个值,该值指示当前流是否支持读取
bool CanSeek 只读属性,获取一个值,该值指示当前流是否支持查找
bool CanWrite 只读属性,获取一个值,该值指示当前流是否支持写入
bool IsAsync 只读属性,获取一个值,该值指示 FileStream 是异步还 是同步打开的
long Length 只读属性,获取用字节表示的流长度
string Name 只读属性,获取传递给构造方法的 FileStream 的名称
long Position 属性,获取或设置此流的当前位置
int Read(byte[] array, int offset, int count) 从流中读取字节块并将该数据写入给定缓冲区中
int ReadByte() 从文件中读取一个字节,并将读取位置提升一个字节
long Seek(lorig offset, SeekOrigin origin) 将该流的当前位置设置为给定值
void Lock(long position, long length) 防止其他进程读取或写入 System.IO.FileStream
void Unlock(long position, long length) 允许其他进程访问以前锁定的某个文件的全部或部分
void Write(byte[] array, int offset, int count) 将字节块写入文件流
void WriteByte(byte value) 将一个字节写入文件流中的当前位置

【实例 】在 D 盘 code 文件夹的 student.txt 文件中写入学生的学号信息,然后再读出。

  • 写入:
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //定义文件路径
            string path = @"D:\\code\\student.txt";
            //创建 FileStream 类的实例
            FileStream fileStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
            //定义学号
            string msg = "1710026";
            //将字符串转换为字节数组
            byte[] bytes = Encoding.UTF8.GetBytes(msg); // 如果是中文的,bytes 数组的长度将不够用,改成使用该语句,将数据从字符串类型转换为字节类型。
            //向文件中写入字节数组
            fileStream.Write(bytes, 0, bytes.Length);
            //刷新缓冲区
            fileStream.Flush();
            //关闭流
            fileStream.Close();
        }
    }
}

-读取:

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //定义文件路径
            string path = @"D:\\code\\student.txt";
            //判断是否含有指定文件
            if (File.Exists(path))
            {
                FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
                //定义存放文件信息的字节数组
                byte[] bytes = new byte[fileStream.Length];
                //读取文件信息
                fileStream.Read(bytes, 0, bytes.Length);
                //将得到的字节型数组重写编码为字符型数组
                char[] c = Encoding.UTF8.GetChars(bytes);
                Console.WriteLine("学生的学号为:");
                //输出学生的学号
                Console.WriteLine(c);
                //关闭流
                fileStream.Close();
            }
            else
            {
                Console.WriteLine("您查看的文件不存在!");
            }
            Console.ReadKey();
        }
    }
}

1.5.3 StreamReader类:读取文件

StreamReader 类用于从流中读取字符串。它继承自 TextReader 类。StreamReader 类中常用的构造方法,如下表所示:

构造方法 说明
StreamReader(Stream stream) 为指定的流创建 StreamReader 类的实例
StreamReader(string path) 为指定路径的文件创建 StreamReader 类的实例
StreamReader(Stream stream, Encoding encoding) 用指定的字符编码为指定的流初始化 StreamReader 类的一个新实例
StreamReader(string path, Encoding encoding) 用指定的字符编码为指定的文件名初始化 StreamReader 类的一个新实例

StreamReader 类中的常用属性和方法如下表所示:

属性或方法 作用
Encoding CurrentEncoding 只读属性,获取当前流中使用的编码方式
bool EndOfStream 只读属性,获取当前的流位置是否在流结尾
void Close() 关闭流
int Peek() 获取流中的下一个字符的整数,如果没有获取到字符, 则返回 -1
int Read() 获取流中的下一个字符的整数
int Read(char[] buffer, int index, int count) 从指定的索引位置开始将来自当前流的指定的最多字符读到缓冲区
string ReadLine() 从当前流中读取一行字符并将数据作为字符串返回
string ReadToEnd() 读取来自流的当前位置到结尾的所有字符

  • 【实例】读取 D 盘 code 文件夹下 test.txt 文件中的信息。
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //定义文件路径
            string path = @"D:\\code\\test.txt";
            //创建 StreamReader 类的实例
            StreamReader streamReader = new StreamReader(path);
            //一直读取文件直到遇到结束符
            while (streamReader.Peek() != -1)
            {
                //读取文件中的一行字符
                string str = streamReader.ReadLine();
                Console.WriteLine(str);
            }
            streamReader.Close(); // 安全关闭流
            Console.ReadKey();
        }
    }
}

1.5.4 StreamWriter类:写入文件

与StreamReader 类相对应,StreamWriter 类主要用于向流中写入数据。StreamWriter 类的一些常用的构造方法,如下表所示:

构造方法 说明
StreamWriter(Stream stream) 为指定的流创建 StreamWriter 类的实例
StreamWriter(string path) 为指定路径的文件创建 StreamWriter 类的实例
StreamWriter(Stream stream, Encoding encoding) 用指定的字符编码为指定的流初始化 StreamWriter 类的一个新实例
StreamWriter(string path, Encoding encoding) 用指定的字符编码为指定的文件名初始化 StreamWriter 类的一个新实例

在创建了 StreamWriter 类的实例后即可调用其类成员,完成向文件中写入信息的操作。

StreamWriter 类中常用的属性和方法如下表所示。

属性或方法 作用
bool AutoFlush 属性,获取或设置是否自动刷新缓冲区
Encoding Encoding 只读属性,获取当前流中的编码方式
void Close() 关闭流
void Flush() 刷新缓冲区
void Write(char value) 将字符写入流中
void WriteLine(char value) 将字符换行写入流中
Task WriteAsync(char value) 将字符异步写入流中
Task WriteLineAsync(char value) 将字符异步换行写入流中

在上表中给出的方法中,Write、WriteAsync、WriteLineAsync 方法还有很多不同类型写入的重载方法,这里没有一一列出。


【实例】向 D 盘 code 文件夹的 test.txt 文件中写入姓名和手机号码。

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"D:\\code\\test.txt";
            //创建StreamWriter 类的实例
            StreamWriter streamWriter = new StreamWriter(path);
            //向文件中写入姓名
            streamWriter.WriteLine("报警电话: ");
            //向文件中写入手机号
            streamWriter.WriteLine("110");
            //刷新缓存
            streamWriter.Flush();
            //关闭流
            streamWriter.Close();
        }
    }
}

1.5.5 BinaryReader类:读取二进制文件

以二进制形式读取数据时使用的是 BinaryReader 类。它提供的构造方法有 3 种,具体的语法形式如下:

1种形式:
BinaryReader(Stream input)   //其中,input 参数是输入流。2种形式:
BinaryReader(Stream input, Encoding encoding)   //其中,input 是指输入流,encoding 是指编码方式。3种形式:
BinaryReader(Stream input, Encoding encoding, bool leaveOpen)  //其中,input 是指输入流,encoding 是指编码方式,leaveOpen 是指在流读取后是否包括流的打开状态。


//创建文件流的实例
FileStream fileStream = new FileStream("D:\\code\\test.txt", FileMode.Open);
BinaryReader binaryReader1 = new BinaryReader(fileStream);
BinaryReader binaryReader2 = new BinaryReader(fileStream, Encoding.UTF8);
BinaryReader binaryReader3 = new BinaryReader(fileStream, Encoding.UTF8, true);

在完成 BinaryReader 类的实例的创建后,即可完成对文件以二进制形式的读取。

BinaryReader 类中的常用属性和方法如下表所示:

属性或方法 作用
int Read() 从指定的流中读取字符
int Read(byte[] buffer, int index, int count) 以 index 为字节数组中的起始点,从流中读取 count 个字节
int Read(char[] buffer, int index, int count) 以 index 为字符数组的起始点,从流中读取 count 个字符
bool ReadBoolean() 从当前流中读取 Boolean 值,并使该流的当前位置提升 1 个字节
byte ReadByte() 从当前流中读取下一个字节,并使流的当前位置提升 1 个字节
byte[] ReadBytes(int count) 从当前流中读取指定的字节数写入字节数组中,并将当前 位置前移相应的字节数
char ReadChar() 从当前流中读取下一个字符,并根据所使用的 Encoding 和从流中读取的特定字符提升流的当前位置
char[] ReadChars(int count) 从当前流中读取指定的字符数,并以字符数组的形式返回 数据,然后根据所使用的 Encoding 和从流中读取的特定字符将当前位置前移
decimal ReadDecimal() 从当前流中读取十进制数值,并将该流的当前位置提升 16 个字节
double ReadDouble() 从当前流中读取 8 字节浮点值,并使流的当前位置提升 8 个字节
short ReadInt16() 从当前流中读取 2 字节有符号整数,并使流的当前位置提升 2 个字节
int ReadInt32() 从当前流中读取 4 字节有符号整数,并使流的当前位置提升 4 个字节
long ReadInt64() 从当前流中读取 8 字节有符号整数,并使流的当前位置提升 8 个字节
sbyte ReadSByte() 从该流中读取 1 个有符号字节,并使流的当前位置提升 1 个字节
float ReadSingle() 从当前流中读取 4 字节浮点值,并使流的当前位置提升 4 个字节
string ReadString() 从当前流中读取一个字符串。字符串有长度前缀,一次 7 位地被编码为整数
ushort ReadUInt16() 从该流中读取的 2 字节无符号整数
uint ReadUInt32() 从该流中读取的 4 字节无符号整数
ulong ReadUInt64() 从该流中读取的 8 字节无符号整数
void FillBuffer(int numBytes) 用从流中读取的指定字节数填充内部缓冲区

在 BinaryReader 类中提供的方法并不是直接读取文件中指定数据类型的值,而是读取由 BinaryWriter 类写入到文件中的。

在上述方法中只有 Read 方法不要求读取的值必须由 BinaryWriter 类写入到文件中。


【实例 】使用 BinaryReader 类读取记事本文件中的信息。

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            FileStream fileStream = new FileStream(@"D:\\code\\test.txt", FileMode.Open);
            BinaryReader binaryReader = new BinaryReader(fileStream);
            //读取文件的一个字符
            int a = binaryReader.Read();
            //判断文件中是否含有字符,若不含字符,a 的值为 -1
            while (a != -1)
            {
                //输出读取到的字符
                Console.Write((char)a);
                a = binaryReader.Read();
            }
            Console.ReadKey();
        }
    }
}

1.5.6 BinaryWriter类:写入二进制数据

BinaryWriter 类用于向流中写入内容,与BinaryReader类似。具体的语法形式如下。

1种形式:
BinaryWriter(Stream output)2种形式:
BinaryWriter(Stream output, Encoding encoding)3种形式:
BinaryWriter(Stream output, Encoding encoding, bool leaveOpen)

BinaryWriter 类中常用的属性和方法如下表所示:

属性或方法 作用
void Close() 关闭流
void Flush() 清理当前编写器的所有缓冲区,使所有缓冲数据写入基础设备
long Seek(int offset, SeekOrigin origin) 返回查找的当前流的位置
void Write(char[] chars) 将字符数组写入当前流
Write7BitEncodedInt(int value) 以压缩格式写出 32 位整数

除了上面的方法以外,Write 方法还提供了多种类型的重载方法。


【实例】在 D 盘 code 文件夹的 test.txt 文件中写入图书的名称和价格,使用 BinaryReader 类读取写入的内容。

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            FileStream fileStream = new FileStream(@"D:\\code\\test.txt", FileMode.Open, FileAccess.Write);
            //创建二进制写入流的实例
            BinaryWriter binaryWriter = new BinaryWriter(fileStream);
            //向文件中写入图书名称
            binaryWriter.Write("C# Tutorial");
            //向文件中写入图书价格
            binaryWriter.Write(49.5);
            //清除缓冲区的内容,将缓冲区中的内容写入到文件中
            binaryWriter.Flush();
            //关闭二进制流
            binaryWriter.Close();
            //关闭文件流
            fileStream.Close();

            fileStream = new FileStream(@"D:\\code\\test.txt", FileMode.Open, FileAccess.Read);
            //创建二进制读取流的实例
            BinaryReader binaryReader = new BinaryReader(fileStream);
            //输出图书名称
            Console.WriteLine(binaryReader.ReadString());
            //输出图书价格
            Console.WriteLine(binaryReader.ReadDouble());
            //关闭二进制读取流
            binaryReader.Close();
            //关闭文件流
            fileStream.Close();
            Console.ReadKey();
        }
    }
}


1.5.7 文本文件的读写

Link

1.5.8 二进制文件的读写

Link

1.5.9 Windows 文件系统的操作

Link

2. 委托和事件


委托和事件在 Windows 窗体应用程序、 ASP.NET 应用程序、WPF 应用程序等应用中是最为普遍的应用。

C# 中的**委托(Delegate)**类似于 C 或 C++ 中函数的指针。委托是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。

事件基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些出现,如系统生成的通知等。

2.1 委托Delegate

委托从字面上理解就是一种代理,类似于房屋中介,由租房人委托中介为其租赁房屋。

在 C# 语言中,委托则委托某个方法来实现具体的功能。

委托是一种引用类型,虽然在定义委托时与方法有些相似,但不能将其称为方法。

从数据结构来讲,委托是和类一样是一种用户自定义类型。

委托是方法的抽象,它存储的就是一系列具有相同签名和返回类型的方法的地址。

调用委托的时候,委托包含的所有方法将被执行。

委托是 C# 语言中的一个特色,通常将委托分为命名方法委托、多播委托、匿名委托,其中命名方法委托是使用最多的一种委托。

2.1.1 命名方法委托

命名方法委托是最常用的一种委托,委托的定义与方法的定义是相似的,其定义的语法形式如下:

修饰符  delegate  返回值类型  委托名 ( 参数列表 );

public delegate void MyDelegate();

实例化委托:

委托名  委托对象名 = new 委托名 ( 方法名 );

命名方法委托在实例化委托时必须带入方法的具体名称。委托中传递的方法名既可以是静态方法的名称,也可以是实例方法的名称。

调用委托:

委托对象名 ( 参数列表 );

【实例】

  • 静态方法的委托
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        public delegate void MyDelegate(); // 定义声明委托
        static void Main(string[] args)
        {
        	// 若使用静态方法,在向委托中传递方法名时只需要用“类名.方法名”的形式
            MyDelegate myDelegate = new MyDelegate(Test.SayHello); // 实例化方法委托
            myDelegate(); // 调用委托
            Console.ReadKey();
        }
    }

    class Test
    {
        public static void SayHello() // 静态方法
        {
            Console.WriteLine("Hello Delegate!");
        }
    }
}

  • 实例方法的委托
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        public delegate void MyDelegate(); // 定义声明委托
        static void Main(string[] args)
        {
            // 若使用实例方法,在向委托中传递方法名时需要通过类的实例来调用方法,即使用“new 类名 (). 方法名”的形式
            MyDelegate myDelegate = new MyDelegate(new Test().SayHello); // 实例化方法委托
            myDelegate(); // 调用委托
            Console.ReadKey();
        }
    }

    class Test
    {
        public void SayHello() // 实例方法
        {
            Console.WriteLine("Hello Delegate!");
        }
    }
}

2.1.2 多播委托

多播委托是指在一个委托中注册多个方法,在注册方法时可以在委托中使用加号运算符或者减号运算符来实现添加或撤销方法。

在现实生活中,多播委托的实例是随处可见的,例如某点餐的应用程序,既可以预定普通的餐饮也可以预定蛋糕、鲜花、水果等商品。
这里委托相当于点餐平台,每一个类型的商品可以理解为在委托上注册的一个方法。

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        public delegate void OrderDelegate(); // 声明委托
        static void Main(string[] args)
        {
            //实例化委托
            OrderDelegate orderDelegate = new OrderDelegate(Order.BuyFood);
            //向委托中注册方法
            orderDelegate += Order.BuyCake;
            orderDelegate += Order.BuyFlower;
            撤销方法注册
            //orderDelegate -= Order.BuyFlower;
            //调用所有方法委托
            orderDelegate();
            Console.ReadKey();
        }
    }

    // 多播委托
    class Order
    {
        public static void BuyFood()
        {
            Console.WriteLine("购买快餐!");
        }
        public static void BuyCake()
        {
            Console.WriteLine("购买蛋糕!");
        }
        public static void BuyFlower()
        {
            Console.WriteLine("购买鲜花!");
        }
    }
}

2.1.3 匿名委托

匿名委托是指使用匿名方法注册在委托上,实际上是在委托中通过定义代码块来实现委托的作用,具体的语法形式如下:

//1. 定义委托
修饰符  delegate  返回值类型  委托名 ( 参数列表 );

//2. 定义匿名委托
委托名  委托对象 = delegate
{
    // ...
};

//3. 调用匿名委托
委托对象名 ( 参数列表 );

【实例】使用匿名委托计算长方形的面积。

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        public delegate void AreaDelegate(double length, double width); // 定义委托
        static void Main(string[] args)
        {
            Console.WriteLine("请输入长方形的长:");
            double length = double.Parse(Console.ReadLine());
            Console.WriteLine("请输入长方形的宽:");
            double width = double.Parse(Console.ReadLine());
            AreaDelegate areaDelegate = delegate // 定义匿名委托
            {
                Console.WriteLine("长方形的面积为:" + length * width);
            };
            areaDelegate(length, width); // 调用匿名委托
            Console.ReadKey();
        }
    }
}


2.2 事件Event

事件是一种引用类型,实际上也是一种特殊的委托。通常,每一个事件的发生都会产生发送方和接收方,发送方是指引发事件的对象,接收方则是指获取、处理事件。事件要与委托一起使用.事件定义的语法形式如下:

访问修饰符  event  委托名  事件名 ;

// 注:由于在事件中使用了委托,因此需要在定义事件前先定义委托。
// 在定义事件后需要定义事件所使用的方法,并通过事件来调用委托。

【实例 1】通过事件完成在控制台上输岀“Hello Event!”的操作。

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        //定义委托
        public delegate void SayDelegate();
        //定义事件
        public event SayDelegate SayEvent;
        //定义委托中调用的方法
        public void SayHello()
        {
            Console.WriteLine("Hello Event!");
        }
        //创建触发事件的方法
        public void SayEventTrigger()
        {
            //触发事件,必须与事件是同名方法
            SayEvent();
        }
        static void Main(string[] args)
        {
            //创建Program类的实例
            Program program = new Program();
            //实例化事件,使用委托指向处理方法
            program.SayEvent = new SayDelegate(program.SayHello);
            //调用触发事件的方法
            program.SayEventTrigger();
            Console.ReadKey();
        }
    }
}


【实例 2】在事件中使用多播委托完成预定不同商品的操作。

using System;
using System.IO;
using System.Collections.Generic;
using System.Threading;
using System.Text;

namespace FileIOApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            //创建MyEvent类的实例
            MyEvent myEvent = new MyEvent();
            //实例化事件,使用委托指向处理方法
            myEvent.BuyEvent += new MyEvent.BuyDelegate(MyEvent.BuyFood);
            myEvent.BuyEvent += new MyEvent.BuyDelegate(MyEvent.BuyCake);
            myEvent.BuyEvent += new MyEvent.BuyDelegate(MyEvent.BuyFlower);
            // 撤销委托指向方法
            //myEvent.BuyEvent -= new MyEvent.BuyDelegate(MyEvent.BuyFlower);
            //调用触发事件的方法
            myEvent.InvokeEvent();
            Console.ReadKey();
        }
    }

    class MyEvent
    {
        //定义委托
        public delegate void BuyDelegate();
        //定义事件
        public event BuyDelegate BuyEvent;
        //定义委托中使用的方法
        public static void BuyFood()
        {
            Console.WriteLine("购买快餐!");
        }
        public static void BuyCake()
        {
            Console.WriteLine("购买蛋糕!");
        }
        public static void BuyFlower()
        {
            Console.WriteLine("购买鲜花!");
        }
        //创建触发事件的方法
        public void InvokeEvent()
        {
            //触发事件,必须和事件是同名方法
            BuyEvent();
        }
    }
}


3. Excel 操作


3.1 添加命名空间引用

  1. 右击“解决方案资源管理器”中的“应用”,选择添加应用;
    C#理论 —— 文件操作、委托和事件_第1张图片

  2. 分别输入搜索excelgraph,勾选最新的版本,点确定;
    C#理论 —— 文件操作、委托和事件_第2张图片

  3. 选择“COM”,搜索以下引用并添加;
    C#理论 —— 文件操作、委托和事件_第3张图片

  4. 返回后检查"应用"下面是否已全部添加成功;
    C#理论 —— 文件操作、委托和事件_第4张图片

  5. 在代码中应用命名空间;

//用到的名空间
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
using Microsoft.Office.Core;//使用Nothing
using System.Runtime.InteropServices;//导入dll

3.2 excel 应用的创建与销毁

            //创建excel应用程序
            Excel.Application myApp = new Excel.Application();
 
            //在这里处理代码
 
            //关闭应用程序
            myApp.Quit();
            System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp);
            myApp = null;

3.3 打开Excel

//打开excel表格
string str = @"E:\LearnExcel\data.xlsx";
Excel.Workbook wb = myApp.Workbooks.Open(str);
Excel.Worksheet ws = myApp.Worksheets.Add();
 
 
 
//在这里编写处理代码....
 
//保存数据表
wb.Save();
//关闭数据表
wb.Close();
 
 //销毁应用
myApp.Quit();
myApp = null;
System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp);

3.4 读取Excel

           string str = @"D:\xxx\dataimport.xlsx";


            //创建excel应用程序
            Excel.Application myApp = new Excel.Application();
            Excel.Worksheet ws = myApp.Worksheets.Add();
            Excel.Workbook wb = myApp.Workbooks.Open(str);

            //处理代码
            textBox1.AppendText("start!");
            Excel.Worksheet ws = wb.Worksheets[1];//sheet的索引从1开始
            //获取工作表的名称
            string wsName = ws.Name;
            textBox1.AppendText("wsName:");
            textBox1.AppendText(wsName);
            System.Threading.Thread.Sleep(500); 
            //数据表的行数
            int wsRows = ws.Rows.Count;
            textBox1.AppendText("wsRows:");
            textBox1.AppendText(Convert.ToString(wsRows));
            System.Threading.Thread.Sleep(500); 
            //数据表的列数
            int wsColumns = ws.Columns.Count;
            textBox1.AppendText("wsColumns:");
            textBox1.AppendText(Convert.ToString(wsColumns));
            System.Threading.Thread.Sleep(500); 
            //数据表的有效数据行数
            int wsUsedRows = ws.UsedRange.Rows.Count;
            textBox1.AppendText("wsUsedRows:");
            textBox1.AppendText(Convert.ToString(wsUsedRows));
            System.Threading.Thread.Sleep(500); 
            //数据表的有效数据列数
            int wsUsedColumns = ws.UsedRange.Columns.Count;
            textBox1.AppendText("wsUsedColumns:");
            textBox1.AppendText(Convert.ToString(wsUsedColumns));
            System.Threading.Thread.Sleep(500);

            textBox1.AppendText("done!");
            //关闭应用程序
            myApp.Quit();
            System.Runtime.InteropServices.Marshal.ReleaseComObject(myApp);
            myApp = null;

*. 参考:

  1. C#编程学习27: C#操作Excel从入门到精通

你可能感兴趣的:(C#,c#,开发语言)