当前位置: 首页 > news >正文

c#之反射详解

总目录


文章目录

  • 总目录
  • 一、反射是什么?
    • 1、C#编译运行过程
    • 2、反射与元数据
    • 3、反射的优缺点
  • 二、反射的使用
    • 1、反射相关的类和命名空间
      • 1、System.Type类的应用
      • 2、System.Activator类的应用
      • 3、System.Reflection.Assembly类的应用
      • 4、System.Reflection.Module类的应用
      • 5、System.AppDomain类的应用
      • 6、dynamic 在反射中的应用
    • 2、反射的应用
      • 1、 数据库辅助类反射
  • 结语


一、反射是什么?

1、C#编译运行过程

说到反射,就不得不说一下C#编译运行过程:

  • 首先我们在VS点击编译的时候,就会将C#源代码编译成程序集

程序集以可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式实现

  • 程序集中包含有Microsoft 中间语言 (MSIL) 和必需的元数据。

元数据存储以下信息:

  • 程序集的说明:标识(名称、版本、区域性、公钥)、导出的类型、该程序集所依赖的其他程序集、运行所需的安全权限。
  • 类型的说明:名称、可见性、基类和实现的接口、成员(方法、字段、属性、事件、嵌套的类型)。
  • 特性:修饰类型和成员的其他说明性元素。
  • 在执行时,实时 (JIT) 编译器将 MSIL 转换为本机代码

运行 Microsoft 中间语言 (MSIL) 前,必须根据公共语言运行时将其编译为目标计算机基础结构的本机代码。

  • 运行代码

公共语言运行时提供启用要发生的托管执行的基础结构以及执行期间可使用的服务

2、反射与元数据

反射 来自 System.Reflection命名空间,它可以读取程序集中的元数据,利用元数据创建对象,从而实现各种功能。

区分 反射 与 反编译,反射读取的是元数据,反编译读取的IL代码

3、反射的优缺点

  • 优点:提高了程序的灵活性和扩展性,降低耦合度
  • 缺点:由于反射多了一道程序,性能上相较于直接代码要慢

对于一些大型的项目,该用反射的地方还是要用,即使牺牲一点性能

二、反射的使用

1、反射相关的类和命名空间

  • 反射命名空间
using System.Reflection;
  • 反射相关的类
System.Type
System.AppDomain
System.Activator
System.Reflection.Assembly
System.Reflection.ModuleSystem.Reflection.ConstructorInfo
System.Reflection.ParameterInfo
System.Reflection.MethodInfo
System.Reflection.PropertyInfo
System.Reflection.FieldInfo
System.Reflection.MemberInfo

1、System.Type类的应用

  • Type类中的基本属性
    在这里插入图片描述

FullName :获取该类型的完全限定名称,包括其命名空间,但不包括程序集

    • Type 中的 Assembly属性
        static void Main(string[] args){Type type1 = typeof(User);//属性Assembly//获取声明该类型的 Assembly。//对于泛型类型,则获取定义该泛型类型的 Assembly。Assembly assembly = type1.Assembly;Console.WriteLine($"{assembly.FullName}");Console.ReadLine();}

通过Type对象我们也可以获取得到在其中申明了该类型的程序集,至于Assembly的用途将在Assembly那一节进行详细介绍

    • Type 中的 AssemblyQualifiedName 属性 和 FullName属性

在这里插入图片描述
上图中:类型的程序集限定名 的格式中的 全名称部分 即是 Type中的FullName属性值
FullName 获取该类型的完全限定名称,包括其命名空间,但不包括程序集

  • Type类的常用的方法
    // UserInfo类是为 介绍 Type类中常用方法 而准备的对象public class UserInfo{private int _num = 0;public string Phone = "1311111111";public string Name { get; set; }public string Address { get; set; }public UserInfo(){Console.WriteLine("UserInfo默认构造函数");}public UserInfo(string name){Console.WriteLine($"UserInfo参数化构造函数:{name}");}public int PublicMethod(){return int.MinValue;}internal void InternalMethod (){ }private void PrivateMethod(){}}
	class Program{static void Main(string[] args){UserInfo userInfo = new UserInfo();//【*】通过System.Object中的GetType()获取Type实例Type type = userInfo.GetType();//GetConstructors()获取所有的公共的构造函数ConstructorInfo[] constructorInfos= type.GetConstructors();foreach (var item in constructorInfos){//GetParameters()获取指定方法或构造函数的参数ParameterInfo[] parameterInfos = item.GetParameters();foreach (var pi in parameterInfos){Console.WriteLine($"{item.Name}:{pi.Name}:{pi.ParameterType}");}              }//获取当前Type 实例的所有Public方法MethodInfo[] methodInfos = type.GetMethods();foreach (var item in methodInfos){               Console.WriteLine($"{type.Name}类型中有:{item.Name}方法,返回类型为{item.ReturnType}");}//获取当前Type 实例的所有Public属性PropertyInfo[] propertyInfos = type.GetProperties();foreach (var item in propertyInfos){Console.WriteLine($"{type.Name}类中有 属性-{item.Name} 类型为-{item.PropertyType}");}//获取当前Type 实例的所有Public字段FieldInfo[] fieldInfos = type.GetFields();foreach (var item in fieldInfos){Console.WriteLine($"{type.Name}类中有 字段-{item.Name} 类型为-{item.FieldType}");}MemberInfo[] memberInfos = type.GetMembers();foreach (var item in memberInfos){Console.WriteLine($"{type.Name}类中有 成员名称-{item.Name} 类型为-{item.MemberType}");}Console.ReadLine();}}

来张 代码贴图可能更为直观
在这里插入图片描述
由上可知,Type类给我们提供了很全面的 类型的 元数据的 获取方式。

    • BindingFlags
            Type type1 = Type.GetType("ConsoleApp1.UserInfo");//GetMembers 中传入 BindingFlags 相当于是对成员信息进行一个过滤//BindingFlags 不仅仅是GetMembers 专有,很多方法中都可以传入BindingFlags进行过滤//BindingFlags 是位标志枚举,可使用 | & ^ 等运算符 | 表示取并集,& 表示取交集,^ 表示取差集//BindingFlags.Public 表示公共成员//BindingFlags.NonPublic 表示非公共成员//BindingFlags.Instance 表示实例成员//BindingFlags.Static 表示静态成员MemberInfo[] memberInfos = type1.GetMembers(BindingFlags.NonPublic|BindingFlags.Instance);foreach (var item in memberInfos){Console.WriteLine($"BindingFlags.NonPublic|BindingFlags.Instance(实例非公共成员)名称:{item.Name}");}

BindingFlags.Instance 和BindingFlags.Static :实例成员是相对于静态成员而言的,多数情况下我们都省略了BindingFlags 这个参数,少数需要筛选成员的时候,传入该参数

  • 获取Type 实例对象的三个方法

在这里插入图片描述

2、System.Activator类的应用

//Activator类主要用于创建对象的实例
Type type = typeof(UserInfo);
UserInfo userInfo=(UserInfo)Activator.CreateInstance(type);

3、System.Reflection.Assembly类的应用

  • 对于程序集的限定名称使用小结

在这里插入图片描述
由上图代码可知:

  • 程序集的显示名称,可通过Assembly.FullNameAssembly.GetName().FullName(即AssemblyName.FullName) 两种方式获取,这种获取的名称,一般是作为 Assembly.Load()的标准参数值
  • 类型的程序集限定名,可通过Type类中的AssemblyQualifiedName属性获取(通常作为Type.GetType()方法中的参数值), 相较于Assembly.FullName,名称格式上多了 Type.FullName 这一部分
  • Assembly类中的常用方法
    • Assembly.Load()方法接收一个String或AssemblyName类型作为参数,这个参数需要程序集的强名称
  • 程序集的强名称:是程序集的FullName(具有名称,版本,语言,公钥标记);
  • 程序集的弱命名:只有程序集名称而没有版本,语言和公钥标记;平常我们创建的一个类库,如果没有特殊操作都属于是是弱名称程序集
  • Load(“强名称程序集”)查找程序集的顺序:首先它会去全局程序集缓存查找,然后到应用程序的根目录查找,最后会到应用程序的私有路径查找。
  • Load(“弱名称程序集”)查找程序集的顺序:首先到应用程序的根目录查找,最后会到应用程序的私有路径查找。
    • Assembly.LoadFrom() 根据程序集的文件名或路径,加载程序集;这个方法会加载此程序集引用的其他程序集
    • Assembly.LoadFile() 加载指定路径上的程序集文件内容,和上面方法的不同之处是这个方法不会加载此程序集引用的其他程序集

在这里插入图片描述
程序集加载的三种方式,可以在项目中添加该程序集的引用后使用,也可在未添加该程序集的情况下使用(某些情况下),这样就极大的丰富的项目的灵活性和扩展性

    • 反射:创建对象的三种方式

在这里插入图片描述

    • 反射: 调用构造函数创建对象详解

在这里插入图片描述

    • 常用方法(包含Type类中的方法)

在这里插入图片描述
Invoke 调用静态方法,对象可以为null ,形如
methodInfo.Invoke(null, new object[] { "Activator.CreateInstance + type.GetMethod" });

		Assembly assembly1 = Assembly.Load("ClassLibrary1");// GetTypes 获取程序集中的所有类型Type[] types = assembly1.GetTypes();foreach (var item in types){if (item.FullName == "ClassLibrary1.Class1"){Activator.CreateInstance(item);}}// 通过反射获取方法,然后执行// GetType("类型全名",是否引发异常,是否忽略大小写)Type type = assembly1.GetType("ClassLibrary1.Class1", false,false);object objt = Activator.CreateInstance(type);MethodInfo methodInfo = type.GetMethod("Show");//通过GetParameters获取方法的参数信息ParameterInfo[] parameterInfos = methodInfo.GetParameters();// 通过Invoke 调用方法// 对于方法而言,Invoke 至少需要传入两个参数,一个参数为 对象实例object,一个参数为方法参数列表 new object[]methodInfo.Invoke(objt,new object[] { "Activator.CreateInstance + type.GetMethod" });// 通过GetProperty 获取指定名称的属性PropertyInfo propertyInfo = type.GetProperty("Name");// SetValue 给属性赋值propertyInfo.SetValue(objt,"测试类");// 通过GetField 获取指定名称的字段FieldInfo fieldInfo = type.GetField("_num",BindingFlags.NonPublic);// SetValue 给属性赋值fieldInfo.SetValue(objt,12);Console.ReadLine();

4、System.Reflection.Module类的应用

暂无

5、System.AppDomain类的应用

一个AppDomain可以包含N个Assembly,一个Assembly可以包含N个Module,而一个Module可以包含N个Type.

暂无

6、dynamic 在反射中的应用

变量可以具有不同的编译时和运行时类型。 编译时类型是源代码中变量的声明或推断类型。 运行时类型是该变量所引用的实例的类型。

        static void Main(string[] args){Type type = typeof(User);object o_user = Activator.CreateInstance(type);//o_user.Show() //不可能通过o_class1 调用Showdynamic d_user = Activator.CreateInstance(type);d_user.Show("sss");//可以通过d_user 调用方法Show//其实o_user 和 d_user得到结果都是一样的,// 但是因为 object 时编译时类型,object本身没有Show方法,因此调用会报错// 而dynamic 是运行时类型,编译状态下会绕过编译器的检查,直到真正运行后才确定其数据类型Console.ReadLine();}

2、反射的应用

1、 数据库辅助类反射

  • 原始情况下,我们写一个简单的SqlServer帮助类
    public class SqlServerHelper{private static readonly string _connectionString = "server=.;database=test;uid=sa;pwd=123";private SqlConnection _sqlConnection;//执行增删改public int ExecDML(string sql){using (_sqlConnection = new SqlConnection(_connectionString)){_sqlConnection.Open();SqlCommand sqlCommand = new SqlCommand(sql,_sqlConnection);return sqlCommand.ExecuteNonQuery();}}//执行查询public DataSet ExecDQL(string sql){using (_sqlConnection=new SqlConnection(_connectionString)){SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sql,_sqlConnection);DataSet dataSet = new DataSet();sqlDataAdapter.Fill(dataSet);return dataSet;}}}//调用SqlServerHelper 中方法class Program{static void Main(string[] args){SqlServerHelper sqlServerHelper = new SqlServerHelper();var data= sqlServerHelper.ExecDQL("select * from userinfo");var userName = data.Tables[0].Rows[0][1];Console.WriteLine(userName.ToString());Console.ReadLine();}}
  • 选择 反射+配置文件的方式 实现,具体实现步骤如下:
    • 1 创建一个接口
    interface IDbHelper{int ExecDML(string sql);DataSet ExecDQL(string sql);}
    • 2 增加配置文件

在这里插入图片描述

    • 3 实现接口
public class SqlServerHelper : IDbHelper{private static readonly string _connectionString = ConfigurationManager.ConnectionStrings["DbConnection"].ToString();public int ExecDML(string sql){using (SqlConnection sqlConnection = new SqlConnection(_connectionString)){sqlConnection.Open();SqlCommand sqlCommand = new SqlCommand(sql, sqlConnection);return sqlCommand.ExecuteNonQuery();}}public DataSet ExecDQL(string sql){using (SqlConnection sqlConnection = new SqlConnection(_connectionString)){SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(sql, sqlConnection);DataSet dataSet = new DataSet();sqlDataAdapter.Fill(dataSet);return dataSet;}}}

这里主要是通过读取配置文件,确定数据库连接字符串:
ConfigurationManager.ConnectionStrings["DbConnection"].ToString();

    • 4 通过反射+配置文件 调用 数据库执行语句的方法
        static void Main(string[] args){string fullName = $"DbHelper.{ConfigurationManager.AppSettings["DbType"].ToString()}";IDbHelper dbHelper = (IDbHelper)Assembly.Load("DbHelper").CreateInstance(fullName);var data = dbHelper.ExecDQL("select * from userinfo");var userName = data.Tables[0].Rows[0][1];Console.WriteLine(userName.ToString());Console.ReadLine();}
  • 从变更使用的数据库为MySql,分析两种方式应对需求的变动
    • 对于原始方法我们需要再重写一个数据库帮助类(如MySqlHelper),然后重新生成帮助类类库文件,最后该调用的代码
    • 如果按照反射+配置文件的方式实现,我们需要实现MySqlHelper类,然后重新生成类库,替换dll文件即可

这个案例只是一个初级的应用,便于理解反射;
反射的应用场景有:IOC容器,MVC框架,ORM,AOP等,因此理解好反射,对于上述知识点的掌握也是有帮助


结语

以上就是本文的内容,希望以上内容可以帮助到您,如文中有不对之处,还请批评指正。


参考资料:
Type类
AssemblyName 类
C#高级–反射详解
C#通过反射调用类及方法
深入浅出C#反射(Reflection)原理和应用场景
C#语法——反射,架构师的入门基础。
C#基础知识学习之 ☀️ | 反射(Reflection) 的含义和用法
最全的 .NET(C#) 反射使用总结
【C#入门详解16】-反射、依赖注入
C#反射-Assembly.Load、LoadFrom与LoadFile进阶

相关文章:

c#之反射详解

总目录 文章目录 总目录一、反射是什么?1、C#编译运行过程2、反射与元数据3、反射的优缺点 二、反射的使用1、反射相关的类和命名空间1、System.Type类的应用2、System.Activator类的应用3、System.Reflection.Assembly类的应用4、System.Reflection.Module类的应用…...

synchronized jvm实现思考

底层实现时,为什么使用了cxq队列和entryList双向链表?这里为什么不跟AQS中使用一个队列就行了,加了一个entryList的目的是为了什么? 个人理解这里多一个entryList,可能是用于减少频繁的cas操作。假设存在很多锁竞争时&…...

【hive基础】hive常见操作速查

文章目录 一. hive变量操作1. 查看当前hive配置信息2. 设置变量3. 修改变量4. 进入hive终端重新加载配置 二. 执行hive sql三. 启动hive 一. hive变量操作 1. 查看当前hive配置信息 # 查看当前所有配置信息 hive > set ;# 查看某一项配置信息 hive >set hive.metastore…...

2024年山东省职业院校技能大赛中职组“网络安全”赛项竞赛试题-A

2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-A 一、竞赛时间 总计:360分钟 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A、B模块 A-1 登录安全加固 180分钟 200分 A-2 本地安全策略设置 A-3 流量完整性保护 A-4 …...

基于51单片机电子钟温度计数码显示设计( proteus仿真+程序+设计报告+讲解视频)

这里写目录标题 ✅1.主要功能:✅讲解视频:✅2.仿真设计✅3. 程序代码✅4. 设计报告✅5. 设计资料内容清单&&下载链接✅[资料下载链接:](https://docs.qq.com/doc/DS0Nja3BaQmVtWUpZ) 基于51单片机电子钟温度检测数码显示设计( proteu…...

jenkins+centos7上传发布net6+gitlab

工作中实践了一下jenkins的操作,所以记录一下这次经验,没有使用到docker 先看下成果: 选择发布项目 选择要发布的分支 构建中 发布成功 开始 首先安装好jenkins并注册自己的jenkins账号 因为我们的项目代码管理使用的是gitlab&#xff0c…...

python趣味编程-5分钟实现一个F1 赛车公路游戏(含源码、步骤讲解)

Python 中的 F1 赛车公路游戏及其源代码 F1 Race Road Game是用Python编程语言开发的,它是一个桌面应用程序。 这款 Python 语言的 F1 赛道游戏可以免费下载开源代码,它是为想要学习 Python 的初学者创建的。 该项目系统使用了 Pygame 和 Random 函数。 Pygame 是一组跨平…...

Kafka快速入门

文章目录 Kafka快速入门1、相关概念介绍前言1.1 基本介绍1.2 常见消息队列的比较1.3 Kafka常见相关概念介绍 2、安装Kafka3、初体验前期准备编码测试配置介绍 bug记录 Kafka快速入门 1、相关概念介绍 前言 在当今信息爆炸的时代,实时数据处理已经成为许多应用程序和…...

基于Pytorch的从零开始的目标检测

引言 目标检测是计算机视觉中一个非常流行的任务,在这个任务中,给定一个图像,你预测图像中物体的包围盒(通常是矩形的) ,并且识别物体的类型。在这个图像中可能有多个对象,而且现在有各种先进的技术和框架来解决这个问…...

interview review

M: intrinsic matrix [ f x s c x 0 f y c y 0 0 1 ] \begin{bmatrix}f_x & s & c_x \\ 0 & f_y & c_y \\ 0 & 0 & 1\end{bmatrix} ​fx​00​sfy​0​cx​cy​1​ ​ ( c x , c y ) (c_x, c_y) (cx​,cy​): camera center in pixels ( f x , f y …...

layui表头多出一列(已解决)

问题描述 :layui表头多出来一列,但是表体没有内容,很影响美观。 好像是原本的表格有滚轮,我操作放大之后滚轮没有了,但是滚轮自带的表头样式还在, 之后手动把这个样式隐藏掉了,代码如下&#xf…...

​LeetCode解法汇总307. 区域和检索 - 数组可修改

目录链接: 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目: https://github.com/September26/java-algorithms 原题链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 描述: 给你一个数…...

Java源码分析:Guava之不可变集合ImmutableMap的源码分析

原创/朱季谦 一、案例场景 遇到过这样的场景&#xff0c;在定义一个static修饰的Map时&#xff0c;使用了大量的put()方法赋值&#xff0c;就类似这样—— public static final Map<String,String> dayMap new HashMap<>(); static {dayMap.put("Monday&q…...

详解自动化测试之 Selenium

目录 1. 什么是自动化 2.自动化测试的分类 3. selenium&#xff08;web 自动化测试工具&#xff09; 1&#xff09;选择 selenium 的原因 2&#xff09;环境部署 3&#xff09;什么是驱动&#xff1f; 4. 一个简单的自动化例子 5.selenium 常用方法 5.1 查找页面元素&…...

vue监听对象属性值变化

一、官方文档 二、实现方法 方法一、直接根据watch来监听 export default {data() {return {object: {username: ,password: }}},watch: {object.username(newVal, oldVal) {console.log(newVal, oldVal)}} }方法二&#xff1a;利用watch和computed来实现监听 利用computed定…...

Unicode编码的emoji表情如何在前端页面展示(未完成)

Unicode编码的emoji表情如何在前端页面展示 一、首先几个定义解决办法 一、首先几个定义 U1F601 和 0x1F601 表示同一个 Unicode 代码点&#xff0c;即笑脸 Emoji 的代码点。它们之间的区别在于表示方式和数据类型。 1.U1F601 是一种常见的表示方式&#xff0c;也称为 “U” 标…...

基于SSM的设备配件管理和设备检修系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

鸿蒙开发|鸿蒙系统项目开发前的准备工作

文章目录 鸿蒙项目开发的基本流程介绍鸿蒙项目开发和其他项目有什么不同成为华为开发者-注册和实名认证1.登录官方网站 鸿蒙项目开发的基本流程介绍 直接上图&#xff0c;简单易懂&#xff01; 整个项目的开发通过4个模块进行&#xff1a;开发准备、开发应用、运行调试测试和发…...

Evil靶场

Evil 1.主机发现 使用命令探测存活主机&#xff0c;80.139是kali的地址&#xff0c;所以靶机地址就是80.134 fping -gaq 192.168.80.0/242.端口扫描 开放80&#xff0c;22端口 nmap -Pn -sV -p- -A 192.168.80.1343.信息收集 访问web界面 路径扫描 gobuster dir -u http…...

第77题. 组合

原题链接&#xff1a;第77题. 组合 全代码&#xff1a; class Solution { private:vector<vector<int>> result; // 存放符合条件结果的集合vector<int> path; // 用来存放符合条件结果void backtracking(int n, int k, int startIndex) {if (path.size() …...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

c#开发AI模型对话

AI模型 前面已经介绍了一般AI模型本地部署&#xff0c;直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型&#xff0c;但是目前国内可能使用不多&#xff0c;至少实践例子很少看见。开发训练模型就不介绍了&am…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用

文章目录 一、背景知识&#xff1a;什么是 B-Tree 和 BTree&#xff1f; B-Tree&#xff08;平衡多路查找树&#xff09; BTree&#xff08;B-Tree 的变种&#xff09; 二、结构对比&#xff1a;一张图看懂 三、为什么 MySQL InnoDB 选择 BTree&#xff1f; 1. 范围查询更快 2…...

【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道

文/法律实务观察组 在债务重组领域&#xff0c;专业机构的核心价值不仅在于减轻债务数字&#xff0c;更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明&#xff0c;合法债务优化需同步实现三重平衡&#xff1a; 法律刚性&#xff08;债…...

TCP/IP 网络编程 | 服务端 客户端的封装

设计模式 文章目录 设计模式一、socket.h 接口&#xff08;interface&#xff09;二、socket.cpp 实现&#xff08;implementation&#xff09;三、server.cpp 使用封装&#xff08;main 函数&#xff09;四、client.cpp 使用封装&#xff08;main 函数&#xff09;五、退出方法…...