C# Windows Forms应用程序-003

目录

项目结构

命名空间和类定义

主要控件

[GroupBox 控件](#GroupBox 控件)

[Label 控件](#Label 控件)

[TextBox 控件](#TextBox 控件)

[Button 控件](#Button 控件)

[OpenFileDialog 控件](#OpenFileDialog 控件)

方法说明

[构造函数 Form1()](#构造函数 Form1())

[Dispose(bool disposing)](#Dispose(bool disposing))

Main()

InitializeComponent()

[button1_Click(object sender, System.EventArgs e)](#button1_Click(object sender, System.EventArgs e))

[button2_Click(object sender, System.EventArgs e)](#button2_Click(object sender, System.EventArgs e))

[button4_Click(object sender, System.EventArgs e)](#button4_Click(object sender, System.EventArgs e))

[button3_Click(object sender, System.EventArgs e)](#button3_Click(object sender, System.EventArgs e))

加密和解密方法

异常处理

项目截图:

项目源码:


项目结构

命名空间和类定义

  • using指令导入所需的命名空间。
  • AesFile命名空间包含整个应用程序。
  • Form1类继承自System.Windows.Forms.Form,是主窗体类。

主要控件

GroupBox 控件

  • groupBox1: 包含加密文件相关的控件。
  • groupBox2: 包含解密文件相关的控件。

Label 控件

  • 提示用户输入相关信息,例如"被加密文件名"、"设定密码"。

TextBox 控件

  • textBox1: 显示被加密文件路径,只读。
  • textBox2: 输入加密后输出文件路径。
  • textBox3 & textBox4: 输入和确认加密密码。
  • textBox6: 输入解密密码。
  • textBox7: 输入解密后输出文件路径。
  • textBox8: 显示已加密文件路径,只读。

Button 控件

  • button1: 浏览并选择被加密文件。
  • button2: 执行加密操作。
  • button3: 执行解密操作。
  • button4: 浏览并选择已加密文件。

OpenFileDialog 控件

  • openFileDialog1: 允许用户选择文件。

方法说明

构造函数 Form1()

  • public Form1()构造函数调用InitializeComponent()方法初始化控件。

Dispose(bool disposing)

  • 清理所有正在使用的资源。
  • 如果 disposingtrue,则释放托管资源。
  • protected override void Dispose(bool disposing)方法负责释放非托管资源

Main()

  • 应用程序的入口点。
  • 启动 Form1 窗口。

InitializeComponent()

  • 自动生成的方法,包含界面元素的设计信息。
  • 不应手动修改此方法的内容。

button1_Click(object sender, System.EventArgs e)

  • 处理"浏览文件"按钮点击事件。
  • 弹出文件对话框让用户选择要加密的文件,并将文件路径显示在 textBox1 中。

button2_Click(object sender, System.EventArgs e)

  • 处理"加密文件"按钮点击事件。
  • 检查用户是否选择了要加密的文件、提供了输出文件名及有效的密码。
  • 根据提供的密码生成 AES密钥。
  • 创建文件流进行加密操作。
  • 使用 EncryptFile进行实际的加密过程。
  • 加密完成后关闭所有流并弹出成功提示。

button4_Click(object sender, System.EventArgs e)

  • 处理"浏览文件"按钮点击事件。
  • 弹出文件对话框让用户选择要解密的文件,并将文件路径显示在 textBox8 中。

button3_Click(object sender, System.EventArgs e)

  • 处理"解密文件"按钮点击事件。
  • 检查用户是否选择了要解密的文件、提供了输出文件名及有效的密码。
  • 根据提供的密码生成 AES密钥。
  • 创建文件流进行解密操作。
  • 使用 DecryptFile进行实际的解密过程。
  • 解密完成后关闭所有流并弹出成功提示。
  • 如果发生异常,则捕获错误并显示错误消息。

加密和解密方法

  • EncryptFile方法使用Rijndael算法对文件进行加密,生成一个盐值并与加密数据一起写入输出文件。
  • DecryptFile方法使用相同的密码和盐值对文件进行解密,将解密后的数据写入输出文件。
  • GenerateSalt方法生成一个随机盐值,增强安全性。

异常处理

  • 在加密和解密过程中捕获异常并提示用户。

项目截图:

项目源码:

cs 复制代码
using System;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;
using System.IO;
using System.Security.Cryptography;

namespace AesFile
{
    /// <summary>
    /// Form1 的摘要说明。
    /// </summary>
    public class Form1 : System.Windows.Forms.Form
    {
        // 定义各种控件变量
        private GroupBox groupBox1; // 用于加密文件设置的组框
        private Label label1; // 显示"被加密文件名"的标签
        private TextBox textBox1; // 显示被加密文件名的文本框
        private TextBox textBox2; // 输入加密输出文件名的文本框
        private Label label2; // 显示"加密输出文件名"的标签
        private TextBox textBox3; // 输入设定密码的文本框
        private Label label3; // 显示"设定密码(大于6位)"的标签
        private TextBox textBox4; // 重复输入设定密码的文本框
        private Label label4; // 显示"重复设定的密码"的标签
        private Button button1; // 浏览文件按钮
        private Button button2; // 加密文件按钮
        private GroupBox groupBox2; // 用于解密文件设置的组框
        private Button button3; // 解密文件按钮
        private Button button4; // 浏览文件按钮
        private TextBox textBox6; // 输入解密密码的文本框
        private Label label6; // 显示"输入密码"的标签
        private TextBox textBox7; // 输入解密输出文件名的文本框
        private Label label7; // 显示"解密输出文件名"的标签
        private TextBox textBox8; // 显示被解密文件名的文本框
        private Label label8; // 显示"被解密文件名"的标签
        private OpenFileDialog openFileDialog1; // 打开文件对话框
        private Container components = null; // 管理窗体组件的对象

        public Form1()
        {
            InitializeComponent(); // 初始化窗体组件
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null)) // 如果正在释放资源并且组件不为空
            {
                components.Dispose(); // 释放所有托管资源
            }
            base.Dispose(disposing); // 调用基类的Dispose方法
        }

        #region Windows 窗体设计器生成的代码
        private void InitializeComponent()
        {
            this.groupBox1 = new GroupBox(); // 实例化groupBox1
            this.button2 = new Button(); // 实例化button2
            this.button1 = new Button(); // 实例化button1
            this.textBox4 = new TextBox(); // 实例化textBox4
            this.label4 = new Label(); // 实例化label4
            this.textBox3 = new TextBox(); // 实例化textBox3
            this.label3 = new Label(); // 实例化label3
            this.textBox2 = new TextBox(); // 实例化textBox2
            this.label2 = new Label(); // 实例化label2
            this.textBox1 = new TextBox(); // 实例化textBox1
            this.label1 = new Label(); // 实例化label1
            this.groupBox2 = new GroupBox(); // 实例化groupBox2
            this.button3 = new Button(); // 实例化button3
            this.button4 = new Button(); // 实例化button4
            this.textBox6 = new TextBox(); // 实例化textBox6
            this.label6 = new Label(); // 实例化label6
            this.textBox7 = new TextBox(); // 实例化textBox7
            this.label7 = new Label(); // 实例化label7
            this.textBox8 = new TextBox(); // 实例化textBox8
            this.label8 = new Label(); // 实例化label8
            this.openFileDialog1 = new OpenFileDialog(); // 实例化openFileDialog1
            this.groupBox1.SuspendLayout(); // 暂停groupBox1布局逻辑
            this.groupBox2.SuspendLayout(); // 暂停groupBox2布局逻辑
            this.SuspendLayout(); // 暂停Form1布局逻辑

            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.button2); // 将button2添加到groupBox1中
            this.groupBox1.Controls.Add(this.button1); // 将button1添加到groupBox1中
            this.groupBox1.Controls.Add(this.textBox4); // 将textBox4添加到groupBox1中
            this.groupBox1.Controls.Add(this.label4); // 将label4添加到groupBox1中
            this.groupBox1.Controls.Add(this.textBox3); // 将textBox3添加到groupBox1中
            this.groupBox1.Controls.Add(this.label3); // 将label3添加到groupBox1中
            this.groupBox1.Controls.Add(this.textBox2); // 将textBox2添加到groupBox1中
            this.groupBox1.Controls.Add(this.label2); // 将label2添加到groupBox1中
            this.groupBox1.Controls.Add(this.textBox1); // 将textBox1添加到groupBox1中
            this.groupBox1.Controls.Add(this.label1); // 将label1添加到groupBox1中
            this.groupBox1.Location = new Point(38, 24); // 设置groupBox1的位置
            this.groupBox1.Name = "groupBox1"; // 设置groupBox1的名称
            this.groupBox1.Size = new Size(509, 209); // 设置groupBox1的大小
            this.groupBox1.TabIndex = 0; // 设置groupBox1的Tab索引
            this.groupBox1.TabStop = false; // 设置groupBox1是否显示边框
            this.groupBox1.Text = "加密文件设置"; // 设置groupBox1的标题文本
            // 
            // button2
            // 
            this.button2.Location = new Point(349, 160); // 设置button2的位置
            this.button2.Name = "button2"; // 设置button2的名称
            this.button2.Size = new Size(80, 23); // 设置button2的大小
            this.button2.TabIndex = 9; // 设置button2的Tab索引
            this.button2.Text = "加密文件"; // 设置button2的文本
            this.button2.Click += new EventHandler(this.button2_Click); // 绑定button2的点击事件处理程序
            // 
            // button1
            // 
            this.button1.Location = new Point(349, 121); // 设置button1的位置
            this.button1.Name = "button1"; // 设置button1的名称
            this.button1.Size = new Size(80, 23); // 设置button1的大小
            this.button1.TabIndex = 8; // 设置button1的Tab索引
            this.button1.Text = "浏览文件"; // 设置button1的文本
            this.button1.Click += new EventHandler(this.button1_Click); // 绑定button1的点击事件处理程序
            // 
            // textBox4
            // 
            this.textBox4.Location = new Point(120, 162); // 设置textBox4的位置
            this.textBox4.Name = "textBox4"; // 设置textBox4的名称
            this.textBox4.PasswordChar = '*'; // 设置textBox4的密码字符
            this.textBox4.Size = new Size(223, 21); // 设置textBox4的大小
            this.textBox4.TabIndex = 7; // 设置textBox4的Tab索引
            // 
            // label4
            // 
            this.label4.Location = new Point(8, 170); // 设置label4的位置
            this.label4.Name = "label4"; // 设置label4的名称
            this.label4.Size = new Size(104, 16); // 设置label4的大小
            this.label4.TabIndex = 6; // 设置label4的Tab索引
            this.label4.Text = "重复设定的密码:"; // 设置label4的文本
            // 
            // textBox3
            // 
            this.textBox3.Location = new Point(120, 123); // 设置textBox3的位置
            this.textBox3.Name = "textBox3"; // 设置textBox3的名称
            this.textBox3.PasswordChar = '*'; // 设置textBox3的密码字符
            this.textBox3.Size = new Size(223, 21); // 设置textBox3的大小
            this.textBox3.TabIndex = 5; // 设置textBox3的Tab索引
            // 
            // label3
            // 
            this.label3.Location = new Point(8, 131); // 设置label3的位置
            this.label3.Name = "label3"; // 设置label3的名称
            this.label3.Size = new Size(128, 16); // 设置label3的大小
            this.label3.TabIndex = 4; // 设置label3的Tab索引
            this.label3.Text = "设定密码(大于6位):"; // 设置label3的文本
            // 
            // textBox2
            // 
            this.textBox2.Location = new Point(120, 80); // 设置textBox2的位置
            this.textBox2.Name = "textBox2"; // 设置textBox2的名称
            this.textBox2.Size = new Size(223, 21); // 设置textBox2的大小
            this.textBox2.TabIndex = 3; // 设置textBox2的Tab索引
            // 
            // label2
            // 
            this.label2.Location = new Point(8, 85); // 设置label2的位置
            this.label2.Name = "label2"; // 设置label2的名称
            this.label2.Size = new Size(104, 16); // 设置label2的大小
            this.label2.TabIndex = 2; // 设置label2的Tab索引
            this.label2.Text = "加密输出文件名:"; // 设置label2的文本
            // 
            // textBox1
            // 
            this.textBox1.BackColor = SystemColors.Control; // 设置textBox1的背景颜色
            this.textBox1.Location = new Point(120, 34); // 设置textBox1的位置
            this.textBox1.Name = "textBox1"; // 设置textBox1的名称
            this.textBox1.ReadOnly = true; // 设置textBox1为只读模式
            this.textBox1.Size = new Size(223, 21); // 设置textBox1的大小
            this.textBox1.TabIndex = 1; // 设置textBox1的Tab索引
            // 
            // label1
            // 
            this.label1.Location = new Point(8, 37); // 设置label1的位置
            this.label1.Name = "label1"; // 设置label1的名称
            this.label1.Size = new Size(96, 16); // 设置label1的大小
            this.label1.TabIndex = 0; // 设置label1的Tab索引
            this.label1.Text = "被加密文件名:"; // 设置label1的文本
            // 
            // groupBox2
            // 
            this.groupBox2.Controls.Add(this.button3); // 将button3添加到groupBox2中
            this.groupBox2.Controls.Add(this.button4); // 将button4添加到groupBox2中
            this.groupBox2.Controls.Add(this.textBox6); // 将textBox6添加到groupBox2中
            this.groupBox2.Controls.Add(this.label6); // 将label6添加到groupBox2中
            this.groupBox2.Controls.Add(this.textBox7); // 将textBox7添加到groupBox2中
            this.groupBox2.Controls.Add(this.label7); // 将label7添加到groupBox2中
            this.groupBox2.Controls.Add(this.textBox8); // 将textBox8添加到groupBox2中
            this.groupBox2.Controls.Add(this.label8); // 将label8添加到groupBox2中
            this.groupBox2.Location = new Point(38, 272); // 设置groupBox2的位置
            this.groupBox2.Name = "groupBox2"; // 设置groupBox2的名称
            this.groupBox2.Size = new Size(509, 198); // 设置groupBox2的大小
            this.groupBox2.TabIndex = 1; // 设置groupBox2的Tab索引
            this.groupBox2.TabStop = false; // 设置groupBox2是否显示边框
            this.groupBox2.Text = "解密文件设置"; // 设置groupBox2的标题文本
            // 
            // button3
            // 
            this.button3.Location = new Point(322, 136); // 设置button3的位置
            this.button3.Name = "button3"; // 设置button3的名称
            this.button3.Size = new Size(80, 23); // 设置button3的大小
            this.button3.TabIndex = 9; // 设置button3的Tab索引
            this.button3.Text = "解密文件"; // 设置button3的文本
            this.button3.Click += new EventHandler(this.button3_Click); // 绑定button3的点击事件处理程序
            // 
            // button4
            // 
            this.button4.Location = new Point(223, 136); // 设置button4的位置
            this.button4.Name = "button4"; // 设置button4的名称
            this.button4.Size = new Size(80, 23); // 设置button4的大小
            this.button4.TabIndex = 8; // 设置button4的Tab索引
            this.button4.Text = "浏览文件"; // 设置button4的文本
            this.button4.Click += new EventHandler(this.button4_Click); // 绑定button4的点击事件处理程序
            // 
            // textBox6
            // 
            this.textBox6.Location = new Point(97, 136); // 设置textBox6的位置
            this.textBox6.Name = "textBox6"; // 设置textBox6的名称
            this.textBox6.PasswordChar = '*'; // 设置textBox6的密码字符
            this.textBox6.Size = new Size(120, 21); // 设置textBox6的大小
            this.textBox6.TabIndex = 5; // 设置textBox6的Tab索引
            // 
            // label6
            // 
            this.label6.Location = new Point(19, 139); // 设置label6的位置
            this.label6.Name = "label6"; // 设置label6的名称
            this.label6.Size = new Size(72, 16); // 设置label6的大小
            this.label6.TabIndex = 4; // 设置label6的Tab索引
            this.label6.Text = "输入密码:"; // 设置label6的文本
            // 
            // textBox7
            // 
            this.textBox7.Location = new Point(120, 93); // 设置textBox7的位置
            this.textBox7.Name = "textBox7"; // 设置textBox7的名称
            this.textBox7.Size = new Size(282, 21); // 设置textBox7的大小
            this.textBox7.TabIndex = 3; // 设置textBox7的Tab索引
            // 
            // label7
            // 
            this.label7.Location = new Point(19, 96); // 设置label7的位置
            this.label7.Name = "label7"; // 设置label7的名称
            this.label7.Size = new Size(104, 16); // 设置label7的大小
            this.label7.TabIndex = 2; // 设置label7的Tab索引
            this.label7.Text = "解密输出文件名:"; // 设置label7的文本
            // 
            // textBox8
            // 
            this.textBox8.BackColor = SystemColors.Control; // 设置textBox8的背景颜色
            this.textBox8.Location = new Point(120, 50); // 设置textBox8的位置
            this.textBox8.Name = "textBox8"; // 设置textBox8的名称
            this.textBox8.ReadOnly = true; // 设置textBox8为只读模式
            this.textBox8.Size = new Size(282, 21); // 设置textBox8的大小
            this.textBox8.TabIndex = 1; // 设置textBox8的Tab索引
            // 
            // label8
            // 
            this.label8.Location = new Point(19, 53); // 设置label8的位置
            this.label8.Name = "label8"; // 设置label8的名称
            this.label8.Size = new Size(96, 16); // 设置label8的大小
            this.label8.TabIndex = 0; // 设置label8的Tab索引
            this.label8.Text = "被解密文件名:"; // 设置label8的文本
            // 
            // openFileDialog1
            // 
            this.openFileDialog1.FileName = "openFileDialog1"; // 设置openFileDialog1的默认文件名
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new Size(6, 14); // 设置窗体自动缩放的基本大小
            this.ClientSize = new Size(587, 500); // 设置窗体的客户区大小
            this.Controls.Add(this.groupBox2); // 将groupBox2添加到窗体中
            this.Controls.Add(this.groupBox1); // 将groupBox1添加到窗体中
            this.Name = "Form1"; // 设置窗体的名称
            this.StartPosition = FormStartPosition.CenterScreen; // 设置窗体启动位置为中心屏幕
            this.Text = "文件加密解密工具"; // 设置窗体的标题文本
            this.groupBox1.ResumeLayout(false); // 恢复groupBox1布局逻辑
            this.groupBox1.PerformLayout(); // 执行groupBox1的布局逻辑
            this.groupBox2.ResumeLayout(false); // 恢复groupBox2布局逻辑
            this.groupBox2.PerformLayout(); // 执行groupBox2的布局逻辑
            this.ResumeLayout(false); // 恢复Form1布局逻辑
        }
        #endregion

        // 处理"浏览文件"按钮点击事件(加密)
        private void button1_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK) // 显示打开文件对话框并检查结果
            {
                textBox1.Text = openFileDialog1.FileName; // 设置textBox1的文本为选定的文件路径
            }
        }

        // 处理"加密文件"按钮点击事件
        private void button2_Click(object sender, EventArgs e)
        {
            string inputFile = textBox1.Text; // 获取被加密文件的路径
            string outputFile = textBox2.Text; // 获取加密后文件的路径
            string password = textBox3.Text; // 获取设定的密码
            string confirmPassword = textBox4.Text; // 获取确认的密码

            if (string.IsNullOrEmpty(inputFile) || string.IsNullOrEmpty(outputFile) || string.IsNullOrEmpty(password))
            {
                MessageBox.Show("请输入所有必要的信息。"); // 提示用户输入所有必要信息
                return;
            }

            if (password.Length < 6)
            {
                MessageBox.Show("密码必须至少6位。"); // 提示密码长度不足
                return;
            }

            if (password != confirmPassword)
            {
                MessageBox.Show("两次输入的密码不一致。"); // 提示密码不一致
                return;
            }

            try
            {
                EncryptFile(inputFile, outputFile, password); // 调用加密文件的方法
                MessageBox.Show("文件已成功加密。"); // 提示文件加密成功
            }
            catch (Exception ex)
            {
                MessageBox.Show( $ "加密失败: {ex.Message}"); // 提示加密失败并显示错误信息            }        }        // 处理"浏览文件"按钮点击事件(解密)        private void button4_Click(object sender, EventArgs e)        {            if (openFileDialog1.ShowDialog() == DialogResult.OK) // 显示打开文件对话框并检查结果            {                textBox8.Text = openFileDialog1.FileName; // 设置textBox8的文本为选定的文件路径            }        }        // 处理"解密文件"按钮点击事件        private void button3_Click(object sender, EventArgs e)        {            string inputFile = textBox8.Text; // 获取被解密文件的路径            string outputFile = textBox7.Text; // 获取解密后文件的路径            string password = textBox6.Text; // 获取输入的密码            if (string.IsNullOrEmpty(inputFile) || string.IsNullOrEmpty(outputFile) || string.IsNullOrEmpty(password))            {                MessageBox.Show("请输入所有必要的信息。"); // 提示用户输入所有必要信息                return;            }            try            {                DecryptFile(inputFile, outputFile, password); // 调用解密文件的方法                MessageBox.Show("文件已成功解密。"); // 提示文件解密成功            }            catch (Exception ex)            {                MessageBox.Show( $ "解密失败: {ex.Message}"); // 提示解密失败并显示错误信息
            }
        }

        // 文件加密方法
        private void EncryptFile(string inputFile, string outputFile, string password)
        {
            using (FileStream inputStream = File.OpenRead(inputFile), outputStream = File.Create(outputFile))
            {
                byte[] salt = GenerateSalt(); // 生成盐值
                RijndaelManaged rijndael = new RijndaelManaged();
                rijndael.KeySize = 256;
                rijndael.BlockSize = 128;
                PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);
                rijndael.Key = pdb.GetBytes(rijndael.KeySize / 8);
                rijndael.IV = pdb.GetBytes(rijndael.BlockSize / 8);

                outputStream.Write(salt, 0, salt.Length); // 写入盐值到输出流
                CryptoStream cryptoStream = new CryptoStream(outputStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
                inputStream.CopyTo(cryptoStream); // 将输入流复制到加密流
                cryptoStream.FlushFinalBlock(); // 刷新加密流
            }
        }

        // 文件解密方法
        private void DecryptFile(string inputFile, string outputFile, string password)
        {
            using (FileStream inputStream = File.OpenRead(inputFile), outputStream = File.Create(outputFile))
            {
                byte[] salt = new byte[16]; // 创建一个数组来存储盐值
                inputStream.Read(salt, 0, salt.Length); // 从输入流中读取盐值
                RijndaelManaged rijndael = new RijndaelManaged();
                rijndael.KeySize = 256;
                rijndael.BlockSize = 128;
                PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);
                rijndael.Key = pdb.GetBytes(rijndael.KeySize / 8);
                rijndael.IV = pdb.GetBytes(rijndael.BlockSize / 8);

                CryptoStream cryptoStream = new CryptoStream(inputStream, rijndael.CreateDecryptor(), CryptoStreamMode.Read);
                cryptoStream.CopyTo(outputStream); // 将解密流复制到输出流
            }
        }

        // 生成盐值的方法
        private byte[] GenerateSalt()
        {
            RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
            byte[] salt = new byte[16];
            rngCsp.GetBytes(salt); // 使用随机数生成器填充盐值
            return salt;
        }
    }
}
相关推荐
破刺不会编程1 分钟前
linux中基础IO(上)
linux·运维·服务器·开发语言
不爱吃饭爱吃菜10 分钟前
uniapp小程序开发,判断跳转页面是否需要登录方法封装
开发语言·前端·javascript·vue.js·uni-app
JuneXcy31 分钟前
班级管理系统
c#
破刺不会编程1 小时前
Linux中基础IO(下)
linux·运维·服务器·开发语言
阮少年、1 小时前
Course 1: Best Practice of RK‘s start Maps SDK for javascript
开发语言·javascript·ecmascript
Kookoos2 小时前
ABP VNext + CRDT 打造实时协同编辑
c#·.net·yjs·crdt·abp vnext
牛马baby2 小时前
Java高频面试之并发编程-23
java·开发语言·面试
北漂老男孩2 小时前
Flink CEP实践总结:使用方法、常见报错、优化与难点应对
大数据·flink·学习方法
五步晦暝3 小时前
【VBA 中GetOpenFilename】常用友好的人机交互文件全路径选择模式
开发语言·人机交互
o0向阳而生0o3 小时前
54、C# 委托 (Delegate)
开发语言·c#·.net