AI教我学编程之C#类的基本概念(1)

前言

在AI教我学编程之C#类型 中,我们学习了C#类型的的基础知识,而正是类型的一种.

目录

  • 区分类和类型

    • 什么是类?
    • 对话AI
    • 追问
    • 实操
  • 追踪属性的使用

    • AI登场
    • 逐步推进
    • 提出疑问
    • 药不能停
    • 终于实现
  • 探索事件的使用

    • 异步/交互操作
    • 耗时操作
    • 提示
  • 总结

区分类和类型

在 C# 中,类是类型的一种。C# 是一种面向对象的编程语言,它的类型系统分为值类型和引用类型。类属于引用类型的范畴,因为它是用户定义的引用类型。

什么是类?

或者说,什么样的构造可以称之为类?
在菜鸟教程上我们可以看到这段话
在这里插入图片描述
我们来看看AI对此会如何回答?

对话AI

程序员

什么样的构造可以称之为类?

在这里插入图片描述

在面向对象编程中,一个类是一个用于创建对象的蓝图或模板。一个类可以包含以下元素:

  1. 成员变量(字段): 用于存储对象的状态或属性。

    class Person
    {
        // 成员变量
        public string Name;
        public int Age;
    }
    
  2. 成员方法: 用于定义对象的行为或操作。

    class Person
    {
        // 成员方法
        public void DisplayInfo()
        {
            Console.WriteLine($"Name: {Name}, Age: {Age}");
        }
    }
    
  3. 构造函数: 用于初始化对象的特定状态。

    class Person
    {
        // 成员变量
        public string Name;
        public int Age;
    
        // 构造函数
        public Person(string name, int age)
        {
            Name = name;
            Age = age;
        }
    }
    
  4. 属性(可选): 提供对成员变量的访问和修改的公共接口。

    class Person
    {
        // 自动属性
        public string Name { get; set; }
        public int Age { get; set; }
    }
    
  5. 事件(可选): 允许对象发布和订阅事件。

    class Button
    {
        // 事件
        public event EventHandler Click;
    
        // 触发事件的方法
        protected virtual void OnClick(EventArgs e)
        {
            Click?.Invoke(this, e);
        }
    }
    

一个类的目的是封装数据和行为,通过创建对象的实例来使用这些数据和行为。上述元素的组合形成了一个完整的类定义。在使用类时,你可以实例化它并调用其中的方法,访问或修改成员变量等。

首先我们看到这几段代码都以class关键字作为开始,就是说,这段代码表示是由class关键字开始,名称为person,由以下三个元素组成

  • 成员变量:
    • Name:用于存储人员的名字。
    • Age:用于存储人员的年龄。
  • 构造函数:
    public Person(string name, int age):构造函数用于在创建 Person 类的实例时初始化对象的状态(名字和年龄)。
  • 成员方法:
    DisplayInfo():成员方法用于显示人员的名字和年龄信息。

那么剩下两个可选项是做什么的呢?我们接着往下看

追问

程序员

你刚刚列出了两个可选项,属性和事件,为什么这两个只是可选项?

在这里插入图片描述

属性和事件在类的设计中是可选的,而不是必需的,这取决于你的设计需求。下面是关于为什么它们是可选项的一些解释:

  1. 属性(Properties):

    • 作用: 属性提供了一种用于访问和修改类的成员变量的公共接口。它们允许你在读取或写入属性值时执行逻辑,并且可以隐藏实际的字段实现。
    • 为何是可选: 有时,一个简单的公共字段足以满足类的需求,而不需要额外的逻辑。在这种情况下,你可以选择跳过属性的使用。
    class Person
    {
        // 公共字段
        public string Name;
        public int Age;
    }
    

    或者使用属性:

    class Person
    {
        // 属性
        public string Name { get; set; }
        public int Age { get; set; }
    }
    
  2. 事件(Events):

    • 作用: 事件允许类发布和订阅特定的行为,通常与异步编程和观察者模式有关。
    • 为何是可选: 如果你的类不需要定义或响应外部的事件,那么你可以省略事件的使用。事件通常在需要处理异步或交互式操作时使用,而在其他情况下可能不是必需的。
    class Button
    {
        // 事件
        public event EventHandler Click;
    
        // 触发事件的方法
        protected virtual void OnClick(EventArgs e)
        {
            Click?.Invoke(this, e);
        }
    }
    

因此,可选性提供了灵活性,使得类的设计能够根据具体需求进行调整,而不必强制要求每个类都具有属性和事件。

为更加直观理解这两个概念,我们来实操一下

实操

1、打开Visual Studio (我的是2010,在这里就用2010来演示)
在这里插入图片描述
2、新建项目
在这里插入图片描述
3、模板选择visual C# Windows—> Windows窗体应用
在这里插入图片描述
4、起好名后进入以下页面
提示:千万别忘了命名规范 哦!

在这里插入图片描述
5、点击左侧边栏工具箱
在这里插入图片描述
6、我们看到工具箱把窗体覆盖住了
在这里插入图片描述
7、不用担心,点击工具箱右上角的倒三角,选择停靠,这样就可以正常显示了
在这里插入图片描述
8、选择工具箱中的工作控件“按钮”,拖动到窗体上
在这里插入图片描述
9、右击按钮,选择属性,我们可以在右下角看到属性栏
在这里插入图片描述

此时,不管我们拖动什么控件到窗体中,单击控件或者窗体,即可看它的属性

在这里插入图片描述

让我们回到刚刚的问题,为什么属性事件可选的?
AI的回答是:在不需要额外逻辑的情况下,可以跳过属性的使用

追踪属性的使用

1、双击按钮进入如下页面
在这里插入图片描述

AI登场

程序员

以上这段代码中,有个按钮,我需要根据这段代码实现点击按钮跳出"青青草原我最狂,舍我其谁懒大王!"的提示文字

在这里插入图片描述

要在点击按钮时弹出提示文字,你需要在 button1_Click 事件处理方法中添加相应的代码。以下是修改后的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FirstProject
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 在按钮点击事件中添加代码
            MessageBox.Show("青青草原我最狂,舍我其谁懒大王!");
        }
    }
}

在这个修改后的代码中,当 button1 被点击时,button1_Click 事件处理方法会弹出一个消息框(MessageBox),显示指定的提示文字。 MessageBox.Show 方法用于在 Windows 窗体应用程序中显示一个简单的消息框。

如下图所示,点击debug旁边的绿色三角开启调试
在这里插入图片描述
画面左上角我们看到,项目正在运行
在这里插入图片描述
点击按钮,跳出提示
在这里插入图片描述

逐步推进

程序员

现在我需要你在代码中添加类的属性的使用

在这里插入图片描述

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FirstProject
{
    public partial class Form1 : Form
    {
        // 新增的属性
        public string Name { get; set; }
        public int Age { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 在按钮点击事件中使用属性
            Name = "青青草原我最狂";
            Age = 25;

            // 弹出消息框显示属性值
            MessageBox.Show($"姓名:{Name}\n年龄:{Age}\n舍我其谁懒大王!");
        }
    }
}

在这个示例中,我添加了两个属性 NameAge,并在按钮点击事件中给它们赋值。然后,通过MessageBox.Show方法显示属性的值。这个例子演示了如何在类中使用属性,以便存储和获取对象的状态。

提出疑问

程序员

这段代码中属性的使用是必须的吗?

在这里插入图片描述

在给定的代码示例中,属性的使用并不是必需的。在C#中,属性是用于封装对象的状态并提供对其进行访问和修改的公共接口的一种方式。在一些情况下,你可能不需要使用属性,尤其是当类的成员变量(字段)可以直接访问而不需要额外的逻辑时。

在给定的代码示例中,如果你只是在按钮点击事件中使用了一次 Name 和 Age,并且没有其他需要对这些值进行封装或加入逻辑的地方,那么使用属性是可选的。你可以直接在 button1_Click 事件中声明局部变量来存储这些值,而无需将它们定义为属性。

以下是示例,演示如何在不使用属性的情况下直接声明并使用局部变量:

using System;
using System.Windows.Forms;

namespace FirstProject
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 直接声明和使用局部变量
            string name = "青青草原我最狂";
            int age = 25;

            // 弹出消息框显示变量值
            MessageBox.Show($"姓名:{name}\n年龄:{age}\n舍我其谁懒大王!");
        }
    }
}

在这个例子中,我直接在 button1_Click 事件中声明了局部变量 name 和 age,并使用它们存储和显示信息,而没有使用属性。

药不能停

紧接着我补充道
程序员

我需要你添加的是必需的属性,根据原有代码重新添加

在这里插入图片描述

如果你希望使用属性,我会在代码中重新添加必需的属性。以下是在原有代码基础上添加必需的属性的修改版本:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FirstProject
{
    public partial class Form1 : Form
    {
        // 必需的属性
        public string Name { get; set; }
        public int Age { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 在按钮点击事件中使用属性
            Name = "青青草原我最狂";
            Age = 25;

            // 弹出消息框显示属性值
            MessageBox.Show($"姓名:{Name}\n年龄:{Age}\n舍我其谁懒大王!");
        }
    }
}

在这个修改后的代码中,我添加了两个名为 Name 和 Age 的属性。这样,你可以在类的其他地方使用这些属性,而不仅仅是在 button1_Click 事件中。这种封装的方式提高了代码的可维护性和灵活性。

终于实现

程序员

根据现有代码写两段代码,第一段代码即原代码,另一段代码则必须使用属性才能实现其逻辑和功能

在这里插入图片描述

代码段一:不使用属性,直接声明并使用局部变量

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FirstProject
{
    public partial class Form1 : Form
    {
        // 必需的属性
        public string Name { get; set; }
        public int Age { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 在按钮点击事件中使用属性
            Name = "青青草原我最狂";
            Age = 25;

            // 弹出消息框显示属性值
            MessageBox.Show($"姓名:{Name}\n年龄:{Age}\n舍我其谁懒大王!");
        }
    }
}

代码段二:使用属性实现逻辑和功能

在这个示例中,我添加了一些逻辑和功能,并使用属性来存储和访问数据:

using System;
using System.Windows.Forms;

namespace FirstProject
{
    public partial class Form1 : Form
    {
        // 使用属性
        public string Name { get; set; }
        public int Age { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // 使用属性存储数据
            Name = "青青草原我最狂";
            Age = 25;

            // 调用带有逻辑的方法
            DisplayInfo();

            // 弹出消息框显示属性值
            MessageBox.Show($"姓名:{Name}\n年龄:{Age}\n舍我其谁懒大王!");
        }

        // 带有逻辑的方法
        private void DisplayInfo()
        {
            Console.WriteLine($"在 DisplayInfo 方法中:姓名:{Name},年龄:{Age}");
            // 可以在这里添加更多的逻辑和功能
        }
    }
}

在这个示例中,button1_Click 事件使用属性 NameAge 存储数据,然后调用了一个带有逻辑的方法 DisplayInfo。在这个方法中,你可以添加更多的逻辑和功能。这种方式充分利用了属性的封装性,使得逻辑更加清晰和可维护。

这里的DisplayInfo方法,就是AI刚刚提到的可以填充额外的逻辑的地方

探索事件的使用

异步/交互操作

程序员

我现在需要在原代码中可以”处理异步或交互式操作“,据此要求完善代码

在这里插入图片描述

如果你想要在原有代码中处理异步或交互式操作,可以使用异步方法和 async/await 关键字。下面是对原有代码的修改,使其能够处理异步操作:

using System;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FirstProject
{
    public partial class Form1 : Form
    {
        // 使用属性
        public string Name { get; set; }
        public int Age { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            // 使用属性存储数据
            Name = "青青草原我最狂";
            Age = 25;

            // 异步操作
            await DisplayInfoAsync();

            // 弹出消息框显示属性值
            MessageBox.Show($"姓名:{Name}\n年龄:{Age}\n舍我其谁懒大王!");
        }

        // 异步方法
        private async Task DisplayInfoAsync()
        {
            // 异步操作,模拟耗时操作
            await Task.Delay(2000);

            // 在异步方法中使用属性
            Console.WriteLine($"在 DisplayInfoAsync 方法中:姓名:{Name},年龄:{Age}");
            // 可以在这里添加更多的异步操作
        }
    }
}

在这个示例中,我将 DisplayInfo 方法改为 DisplayInfoAsync,并在其内部添加了一个异步操作 await Task.Delay(2000) 以模拟耗时操作。同时,button1_Click 事件也被标记为 async,并在其中使用了 await 关键字来等待异步方法的完成。这样,你可以在异步方法中执行可能涉及等待的耗时操作,而不会阻塞主线程。

耗时操作

程序员

在你刚才给出的代码中,可以执行哪些”耗时操作“,告诉我,并且将这个操作添加到代码中,使我在调试时可以明显的看到”耗时“这一效果

在这里插入图片描述

在我之前提供的代码示例中,我添加了一个模拟耗时操作的异步方法 DisplayInfoAsync,其中使用了 await Task.Delay(2000) 来模拟一个持续两秒的异步操作。这个延迟操作是一个简单的例子,用来模拟可能的耗时操作。

在实际的应用中,你可以将异步方法中的 Task.Delay 替换为真实的耗时操作,比如从数据库中读取数据、进行网络请求、进行复杂的计算等。以下是对代码的修改,其中包含了一个更具体的耗时操作示例:

using System;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace FirstProject
{
    public partial class Form1 : Form
    {
        // 使用属性
        public string Name { get; set; }
        public int Age { get; set; }

        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            // 使用属性存储数据
            Name = "青青草原我最狂";
            Age = 25;

            // 调用带有耗时操作的异步方法
            await DisplayInfoWithDelayAsync();

            // 弹出消息框显示属性值
            MessageBox.Show($"姓名:{Name}\n年龄:{Age}\n舍我其谁懒大王!");
        }

        // 带有耗时操作的异步方法
        private async Task DisplayInfoWithDelayAsync()
        {
            Console.WriteLine("开始执行耗时操作...");

            // 模拟从数据库中读取数据,等待2秒钟
            await Task.Delay(2000);

            // 在异步方法中使用属性
            Console.WriteLine($"在 DisplayInfoWithDelayAsync 方法中:姓名:{Name},年龄:{Age}");
            Console.WriteLine("耗时操作完成。");
        }
    }
}

在这个修改后的代码中,我添加了一段输出,用于在控制台中显示耗时操作的开始和结束。当你点击按钮时,可以在输出中看到开始和结束的标志,这段时间模拟了耗时操作的执行时间。在实际应用中,你可以将这里的 Task.Delay 替换为你真实的耗时操作。

提示

以上代码在VS2010中不可用,在VS2019及以上中可用

VS2010

在这里插入图片描述
VS2019
在这里插入图片描述
开启调试后,我们点击按钮

在这里插入图片描述
两秒钟后,跳出提示
在这里插入图片描述

总结

又是仓促的一天,现在上传的这个版本有些瑕疵,明天我会补好,感谢各位的阅读,喜欢本文的话,点赞收藏关注支持一下博主吧!感谢厚爱!
回到顶部

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/330434.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IO网络4.0

思维导图 tftp上传 #include <myhead.h>#define ERR_LOG(msg) do{\perror(msg);\printf("%d %s %s\n", __LINE__, __func__, __FILE__);\ }while(0)#define PORT 69 #define N 516int do_upload(int sfd, struct sockaddr_in sin);int main(int a…

k8s的对外服务ingress

1、service的作用体现在两个方面 &#xff08;1&#xff09;集群内部&#xff1a;不断跟踪pod的变化&#xff0c;更新deployment中的pod对象&#xff0c;基于pod的ip地址不断变化的一种服务发现机制 &#xff08;2&#xff09;集群外部&#xff1a;类似于负载均衡器&#xff…

PuTTY的ppk密钥与OpenSSH密钥之间的相互转换

几个概念说明&#xff1a;id_rsa、id_rsa.pub、ppk、pem 目前有两个主流的密钥格式&#xff1a;OpenSSH格式的密钥 和 PuTTY格式的密钥。 id_rsa和id_rsa.pub 都是OpenSSH格式的密钥。 id_rsa是OpenSSH格式的SSH私钥。 id_rsa.pub是OpenSSH格式的SSH公钥。ppk文件 ppk文件是P…

机器视觉系统在汽车车轮毂检测上的应用

将机器视觉用于轮毂检测&#xff0c;可以利用图像分析的方法来测量轮毂特征尺寸、判断轮毂形状&#xff0c;并获取其位置坐标等信息&#xff0c;从而能够辨识流水生产线上的各种款式和型号的汽车轮毂。 市面上对汽车车轮毂具体检测要求如下 &#xff1a; 1.为了分辨流水线上…

HTTPS:如何确保您的网站数据传输安全?

目录 博客前言 一.HTTPS 1.1 HTTPS简介 1.2 HTTP和HTTPS区别 1.3 TLS/SSL协议工作原理 1.3.1 TLS/SSL协议结构 1.3.2 SSL/TLS握手协议建立连接过程 1.2.3 SSL/TLS报文分析 博客前言 以下是一个关于HTTPS协议的博客前言示例&#xff1a; 欢迎来到我的博客&#xff0c;今…

2024年腾讯云轻量服务器和CVM云服务器性能如何?

腾讯云轻量服务器和云服务器有什么区别&#xff1f;为什么轻量应用服务器价格便宜&#xff1f;是因为轻量服务器CPU内存性能比云服务器CVM性能差吗&#xff1f;轻量应用服务器适合中小企业或个人开发者搭建企业官网、博客论坛、微信小程序或开发测试环境&#xff0c;云服务器CV…

【面试】测试/测开(ING3)

190. 栈和堆在内存管理上的区别 栈 1&#xff09; 栈是由系统自动分配和回收的内存。 2&#xff09;栈的存储地址是由高地址向低地址扩展的。 3&#xff09;栈是一个先进后出的结构。 4&#xff09;栈的空间大小是一个在编译时确定常数&#xff0c;即栈的大小是有限制的&#x…

2024年回炉计划之排序算法(一)

算法是计算机科学和信息技术中的重要领域&#xff0c;涉及到问题求解和数据处理的方法。要学习算法&#xff0c;你可能需要掌握以下一些基本知识&#xff1a; 基本数据结构&#xff1a; 了解和熟练使用各种数据结构&#xff0c;如数组、链表、栈、队列、树和图等。数据结构是算…

新能源汽车智慧充电桩方案:基于视频监控的可视化智能监管平台

一、方案概述 TSINGSEE青犀&触角云新能源汽车智慧充电桩方案围绕互联网、物联网、车联网、人工智能、视频技术、大数据、4G/5G等技术&#xff0c;结合云计算、移动支付等&#xff0c;实现充电停车一体化、充电桩与站点管理等功能&#xff0c;达到充电设备与站点的有效监控…

【汇编】实验12 编写0号中断的处理程序

记录一下代码 assume cs:code code segment start:mov ax,csmov ds,axmov si,offset do0mov ax,0mov es,axmov di,200hmov cx,offset do0end-offset do0cldrep movsb ;将ds:si的字节单元byte送入es:di&#xff0c;也就是将从do0处往下的指令复制到0:200h中。mov word ptr es:[…

阿赵UE学习笔记——11、地形系统

阿赵UE学习笔记目录 大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的用法&#xff0c;这次来学习一下虚幻引擎的地形系统的用法。 一、创建地形 在选项模式里面&#xff0c;选择地形&#xff1a; 进入到地形界面之后&#xff0c;需要先创建一个地形&#xff1a; 留意看…

Springboot+vue的智能家居系统(有报告),Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的智能家居系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的智能家居系统&#xff0c;采用M&#xff08;model&a…

【Android】为什么在子线程中更新UI不会抛出异常

转载请注明来源&#xff1a;https://blog.csdn.net/devnn/article/details/135638486 前言 众所周知&#xff0c;Android App在子线程中是不允许更新UI的&#xff0c;否则会抛出异常&#xff1a; android.view.ViewRootImpl$CalledFromWrongThreadException: Only the origin…

芯片新闻-Global Semiconductor Sales Increase 5.3% Year-to-Year in November

11 月标志着一年多以来市场同比增长的第一个月&#xff1b;全球芯片销量环比增长2.9% 华盛顿——一月。 2024 年 12 月 9 日——半导体行业协会 (SIA) 今天宣布&#xff0c;2023 年 11 月全球半导体行业销售额总计 480 亿美元&#xff0c;比 2022 年 11 月的 456 亿美元总额增…

rust获取本地外网ip地址的方法

大家好&#xff0c;我是get_local_info作者带剑书生&#xff0c;这里用一篇文章讲解get_local_info的使用。 get_local_info是什么&#xff1f; get_local_info是一个获取linux系统信息的rust三方库&#xff0c;并提供一些常用功能&#xff0c;目前版本0.2.4。详细介绍地址&a…

大屏数据可视化的设计流程及原则

随着数字经济的快速发展和信息化在各行业各领域的深入推进&#xff0c;可视化大屏在各行各业得到越来越广泛的应用。可视化大屏不再只是电影里奇幻的画面&#xff0c;而是被实实在在地应用在政府、商业、金融、制造、交通、城市等各个行业的业务场景中&#xff0c;切切实实地实…

「alias」Linux 给命令起别名,自定义bash命令

0. 背景 Arch 系统没有 ll命令,在其他发行版用惯了一时间没有真不习惯,来配置一下吧! 1. 全局配置 我希望 ll 命令可以被所有人使用,所以应该配置在全局的bash配置文件中,一般这个全局bash配置文件在: /etc/bash.bashrc 切好管理员权限后,命令如下 echo “alias ll‘ls -l -…

React的合成事件

合成事件&#xff1a;通过事件委托&#xff0c;利用事件传播机制&#xff0c;当事件传播到document时&#xff0c;再进行分发到对应的组件&#xff0c;从而触发对应所绑定的事件&#xff0c;然后事件开始在组件树DOM中走捕获冒泡流程。 原生事件 —— > React事件 —— >…

TMDB电影数据分析(下)

TMDB电影数据分析&#xff08;下&#xff09; 本文对源自Kaggle TMDB电影数据集进行分析影响电影票房的因素&#xff0c;数据分析流程包含数据集概分析、数据清洗、数据统计以及分析影响电影票房的因素。影响票房因素可能是电影预算、电影类型、电影时长、受欢迎程度、电影评分…

十二、Qt 操作PDF文件(2)

一、在《十、Qt 操作PDF文件-CSDN博客》中我们用Poppler类库打开了PDF文件&#xff0c;并显示到窗体上&#xff0c;但只能显示一页&#xff0c;功能还没完善&#xff0c;在本章节中&#xff0c;加入了&#xff1a; 通过选择框选择PDF文件并打开&#xff0c;默认打开第一页。通…