当前位置:
首页 > Python基础教程 >
-
C#教程之程序集和反射(C#)
这里我又唠叨几句,大家在学习的时候,如看书或者看视频时觉得非常爽,因为感觉基本都看得懂也都挺容易的,其实看懂是一回事,你自己会动手做出来是一回事,自己能够说出来又是另一回事了。应该把学到的东西变成自己的东西,而不是依样画瓢。
在说反射之前,我们先来了解一下什么是程序集?
程序集包含:
exe可以运行,dll不能直接运行,因为exe中有一个main函数(入口函数)。
类型元数据这些信息可以通过AssemblyInfo.cs文件来自定义。在每一个.net项目中都存在一个AssemblyInfo.cs文件,代码格式:
这些信息在哪里体现呢?就在我们程序集的属性当中进行体现
我们平时在安装一些CS客户端程序的时候,在安装目录下面会看见许多的程序集文件。
使用程序集的好处
直接添加程序集路径或者添加解决方案中的项目引用。
当我们需要扩展一个程序的时候,你可能会直接在原有的项目中进行添加,那这样的话,如果你的这些代码想共享给别人使用呢?你就可以打包成一个程序集,然后别人只要通过引用你这个程序集就可以进行扩展了。像我们常见的.net第三方框架库,如log4net、unity等等。
注意:不能添加循环引用
反射技术其实就是动态获取程序集的元数据的功能,反射通过动态加载dll,然后对其进行解析,从而创建对象,调用成员。
Type是对类的描述,Type类是实现反射的一个重要的类,通过它我们可以获取类中的所有信息,包括方法、属性等。可以动态调用类的属性、方法。
反射的出现让创建对象的方式发生了改变,因为过去面完创建对象都是直接通过new。
dll里面有两部分东西:IL中间语言和metadate元素据。
在.NET中反射用到命名空间是System.Reflection,这里我先通过一个Demo来看反射能做些什么
1、 新建控制台项目ReflectedDemo
2、 新建类库项目My.Sqlserver.Dal
新建两个类SqlServerHelper和SqlCmd,前者为共有类,后者为私有类
3、 项目ReflectedDemo,添加My.Sqlserver.Dal的项目引用,我这样做的目的是为了方便项目ReflectedDemo中的bin目录中时刻存在My.Sqlserver.Dal.dll程序集。
他们之间的区别
ass.CreateInstance(string typeName) 会动态调用类的无参构造函数创建一个对象,返回值就是创建的对象,如果没有无参构造函数就会报错。
如果我们来修改SqlServerHelper类的代码,添加如下构造函数:
这个时候再来运行创建实例的代码就会报错了,而编译时是不报错的。
所以我们一般推荐使用Activator.CreateInstance方法来创建反射对象,因为此方法有许多重载,支持将参数传递给构造函数。
此时再调用就不会出现异常了。
Type类中有三个用得比较多的方法:
它们常用的原因是我们通过反射可以取到的东西太多了,我们需要对数据进行过滤。
添加类BaseSql,让类SqlServerHelper继承自BaseSql
然后查看调用代码:
项目中常用的利用反射来动态切换数据库Demo:
新建类库项目My.Sql.IDal,并添加接口ISqlHelper。通过接口来实现数据库操作的类的解耦,因为接口是抽象的。
添加类库项目My.MySql.Dal,并新增类MySqlHelper.cs
My.Sqlserver.Dal、My.MySql.Dal项目分别添加对项目My.Sql.IDal的引用。让SqlServerHelper继承自接口ISqlHelper
添加App.config配置项
ReflectedDemo项目中Program.cs调用代码:
这样每次需要切换数据库时,只要修改配置文件就可以了。
项目结构:
注意:反射虽然很强大,但却是比较耗性能的,所以一般和缓存结合起来使用。
项目源码:ReflectedDemo.zip
在说反射之前,我们先来了解一下什么是程序集?
程序集
程序集是.net中的概念,程序集可以看作是给一堆相关类打一个包,相当于java中的jar包。程序集包含:
- 资源文件
- 类型元数据(描述在代码中定义的每一类型和成员,二进制形式)
- IL代码(这些都被封装在exe或dll中)
exe可以运行,dll不能直接运行,因为exe中有一个main函数(入口函数)。
类型元数据这些信息可以通过AssemblyInfo.cs文件来自定义。在每一个.net项目中都存在一个AssemblyInfo.cs文件,代码格式:
using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // 有关程序集的常规信息通过以下 // 特性集控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyTitle("ReflectedDemo")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ReflectedDemo")] [assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // 将 ComVisible 设置为 false 使此程序集中的类型 // 对 COM 组件不可见。 如果需要从 COM 访问此程序集中的类型, // 则将该类型上的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID [assembly: Guid("7674d229-9929-4ec8-b543-4d05c6500863")] // 程序集的版本信息由下面四个值组成: // // 主版本 // 次版本 // 生成号 // 修订号 // // 可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值, // 方法是按如下所示使用“*”: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
我们平时在安装一些CS客户端程序的时候,在安装目录下面会看见许多的程序集文件。
使用程序集的好处
- 程序中只引用必须的程序集,减小程序的尺寸。
- 程序集可以封装一些代码,只提供必要的访问接口。
- 方便扩展。
直接添加程序集路径或者添加解决方案中的项目引用。
当我们需要扩展一个程序的时候,你可能会直接在原有的项目中进行添加,那这样的话,如果你的这些代码想共享给别人使用呢?你就可以打包成一个程序集,然后别人只要通过引用你这个程序集就可以进行扩展了。像我们常见的.net第三方框架库,如log4net、unity等等。
注意:不能添加循环引用
什么是添加循环引用?就是说A项目如果添加了B项目的项目引用,那么此时B项目不能再添加A项目的项目引用,也就是说添加项目引用时,必须是单向的,像我们常见的三层框架之间的项目引用。
反射
关于反射,你只要是做.net开发,你就一定天天在用。因为VS的智能提示就是通过应用了反射技术来实现的,还有我们常用的反编译神器Reflector.exe,看它的名字就知道了。项目中比较常见的,是通过结合配置文件来动态实例化对象,如切换数据库实例,或者Sprint.net的通过配置文件来实现依赖注入等。反射技术其实就是动态获取程序集的元数据的功能,反射通过动态加载dll,然后对其进行解析,从而创建对象,调用成员。
Type是对类的描述,Type类是实现反射的一个重要的类,通过它我们可以获取类中的所有信息,包括方法、属性等。可以动态调用类的属性、方法。
反射的出现让创建对象的方式发生了改变,因为过去面完创建对象都是直接通过new。
dll里面有两部分东西:IL中间语言和metadate元素据。
在.NET中反射用到命名空间是System.Reflection,这里我先通过一个Demo来看反射能做些什么
1、 新建控制台项目ReflectedDemo
2、 新建类库项目My.Sqlserver.Dal
新建两个类SqlServerHelper和SqlCmd,前者为共有类,后者为私有类
namespace My.Sqlserver.Dal { public class SqlServerHelper { private int age = 16; public string Name { get; set; } public string Query() { return string.Empty; } } class SqlCmd { } }
3、 项目ReflectedDemo,添加My.Sqlserver.Dal的项目引用,我这样做的目的是为了方便项目ReflectedDemo中的bin目录中时刻存在My.Sqlserver.Dal.dll程序集。
using System; using System.Reflection; namespace ReflectedDemo { class Program { static void Main(string[] args) { //加载程序集文件,在bin目录中查找 Assembly assembly = Assembly.Load("My.Sqlserver.Dal"); Console.WriteLine("----------------Modules----------------------"); var modules = assembly.GetModules(); foreach(var module in modules) { Console.WriteLine(module.Name); } Console.WriteLine("----------------Types----------------------"); var types = assembly.GetTypes(); //获取程序集中所有的类型,包括公开的和不公开的 foreach(var type in types) { Console.WriteLine(type.Name); Console.WriteLine(type.FullName); var members= type.GetMembers(); //获取Type中所有的公共成员 Console.WriteLine("----------------members----------------------"); foreach(var m in members) { Console.WriteLine(m.Name); } } Console.WriteLine("----------------GetExportedTypes----------------------"); var exportedTypes = assembly.GetExportedTypes(); //获取程序集中所有的公共类型 foreach(var t in exportedTypes) { Console.WriteLine(t.Name); } Console.WriteLine("----------------GetType----------------------"); var typeName= assembly.GetType("SqlServerHelper");//获取程序集中指定名称的类型对象 Console.WriteLine(typeName.Name); } } }
动态创建对象
通过ass.CreateInstance(string typeName) 和Activator.CreateInstance(Type t)方法他们之间的区别
ass.CreateInstance(string typeName) 会动态调用类的无参构造函数创建一个对象,返回值就是创建的对象,如果没有无参构造函数就会报错。
Assembly assembly = Assembly.Load("My.Sqlserver.Dal"); object obj = assembly.CreateInstance("My.Sqlserver.Dal.SqlServerHelper"); Console.WriteLine(obj.GetType().ToString());
如果我们来修改SqlServerHelper类的代码,添加如下构造函数:
public SqlServerHelper(int age) { this.age = age; }
所以我们一般推荐使用Activator.CreateInstance方法来创建反射对象,因为此方法有许多重载,支持将参数传递给构造函数。
此时再调用就不会出现异常了。
Type类中有三个用得比较多的方法:
- bool IsAssignableFrom(Type t):是否可以从t赋值,判断当前的类型变量是不是可以接受t类型变量的赋值。
- bool IsInstanceOfType(object o):判断对象o是否是当前类的实例,当前类可以是o的类、父类、接口
- bool IsSubclassOf(Type t):判断当前类是否是t的子类
它们常用的原因是我们通过反射可以取到的东西太多了,我们需要对数据进行过滤。
添加类BaseSql,让类SqlServerHelper继承自BaseSql
然后查看调用代码:
bool result = typeof(BaseSql).IsAssignableFrom(typeof(SqlServerHelper)); Console.WriteLine(result);
SqlServerHelper _SqlServerHelper = new SqlServerHelper(1); bool result = typeof(SqlServerHelper).IsInstanceOfType(_SqlServerHelper); Console.WriteLine(result);
SqlServerHelper _SqlServerHelper = new SqlServerHelper(1); bool result = typeof(SqlServerHelper).IsSubclassOf(typeof(BaseSql)); Console.WriteLine(result);
项目中常用的利用反射来动态切换数据库Demo:
新建类库项目My.Sql.IDal,并添加接口ISqlHelper。通过接口来实现数据库操作的类的解耦,因为接口是抽象的。
public interface ISqlHelper { string Query(); }
My.Sqlserver.Dal、My.MySql.Dal项目分别添加对项目My.Sql.IDal的引用。让SqlServerHelper继承自接口ISqlHelper
public class MySqlHelper : ISqlHelper { public string Query() { return this.GetType().ToString(); } } public class SqlServerHelper :ISqlHelper { private int age = 16; public string Name { get; set; } public string Query() { return this.GetType().ToString(); } }
<appSettings> <add key="DBName" value="My.Sqlserver.Dal,SqlServerHelper"/> </appSettings>
string str = ConfigurationManager.AppSettings["DBName"]; string strAssembly = str.Split(',')[0]; string strClass=str.Split(',')[1]; Assembly assembly = Assembly.Load(strAssembly); Type t = assembly.GetType(strAssembly + "." + strClass); ISqlHelper obj = Activator.CreateInstance(t) as ISqlHelper; Console.WriteLine(obj.Query());
这样每次需要切换数据库时,只要修改配置文件就可以了。
项目结构:
注意:反射虽然很强大,但却是比较耗性能的,所以一般和缓存结合起来使用。
项目源码:ReflectedDemo.zip
博客地址: | http://www.cnblogs.com/jiekzou/ | |
博客版权: |
本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。 如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步! 再次感谢您耐心的读完本篇文章。 |
|
其它: |
.net-QQ群4:612347965 java-QQ群:805741535 H5-QQ群:773766020
我的拙作《ASP.NET MVC企业级实战》已经出版,希望大家多多支持!
|
栏目列表
最新更新
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
Python初学者友好丨详解参数传递类型
如何有效管理爬虫流量?
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比
一款纯 JS 实现的轻量化图片编辑器
关于开发 VS Code 插件遇到的 workbench.scm.
前端设计模式——观察者模式
前端设计模式——中介者模式
创建型-原型模式