程序集和反射(C#)
这里我又唠叨几句,大家在学习的时候,如看书或者看视频时觉得非常爽,因为感觉基本都看得懂也都挺容易的,其实看懂是一回事,你自己会动手做出来是一回事,自己能够说出来又是另一回事了。应该把学到的东西变成自己的东西,而不是依样画瓢。
在说反射之前,我们先来了解一下什么是程序集?
程序集
程序集是.net中的概念,程序集可以看作是给一堆相关类打一个包,相当于java中的jar包。
程序集包含:
- 资源文件
- 类型元数据(描述在代码中定义的每一类型和成员,二进制形式)
- IL代码(这些都被封装在exe或dll中)
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 = ;
public string Name { get; set; }
public string Query()
{
return string.Empty;
}
}
class SqlCmd
{
}
}
Normal
0
7.8 磅
0
2
false
false
false
EN-US
ZH-CN
X-NONE
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.5pt;
mso-bidi-font-size:11.0pt;
font-family:"Calibri","sans-serif";
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-font-kerning:1.0pt;}
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的子类
Type类中还有一个IsAbstract属性:判断是否为抽象的,包含接口。
它们常用的原因是我们通过反射可以取到的东西太多了,我们需要对数据进行过滤。
添加类BaseSql,让类SqlServerHelper继承自BaseSql
然后查看调用代码:
bool result = typeof(BaseSql).IsAssignableFrom(typeof(SqlServerHelper));
Console.WriteLine(result);
SqlServerHelper _SqlServerHelper = new SqlServerHelper();
bool result = typeof(SqlServerHelper).IsInstanceOfType(_SqlServerHelper);
Console.WriteLine(result);
SqlServerHelper _SqlServerHelper = new SqlServerHelper();
bool result = typeof(SqlServerHelper).IsSubclassOf(typeof(BaseSql));
Console.WriteLine(result);
项目中常用的利用反射来动态切换数据库Demo:
新建类库项目My.Sql.IDal,并添加接口ISqlHelper。通过接口来实现数据库操作的类的解耦,因为接口是抽象的。
public interface ISqlHelper
{
string Query();
}
添加类库项目My.MySql.Dal,并新增类MySqlHelper.cs
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 = ;
public string Name { get; set; }
public string Query()
{
return this.GetType().ToString();
}
}
添加App.config配置项
<appSettings>
<add key="DBName" value="My.Sqlserver.Dal,SqlServerHelper"/>
</appSettings>
ReflectedDemo项目中Program.cs调用代码:
string str = ConfigurationManager.AppSettings["DBName"];
string strAssembly = str.Split(',')[];
string strClass=str.Split(',')[];
Assembly assembly = Assembly.Load(strAssembly);
Type t = assembly.GetType(strAssembly + "." + strClass);
ISqlHelper obj = Activator.CreateInstance(t) as ISqlHelper;
Console.WriteLine(obj.Query());
这样每次需要切换数据库时,只要修改配置文件就可以了。
项目结构:
注意:反射虽然很强大,但却是比较耗性能的,所以一般和缓存结合起来使用。
项目源码:ReflectedDemo.zip
Normal
0
7.8 磅
0
2
false
false
false
EN-US
ZH-CN
X-NONE
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:普通表格;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-priority:99;
mso-style-parent:"";
mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
mso-para-margin:0cm;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.5pt;
mso-bidi-font-size:11.0pt;
font-family:"Calibri","sans-serif";
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-font-kerning:1.0pt;}
程序集和反射(C#)的更多相关文章
- .net学习之泛型、程序集和反射
一.泛型1.CLR编译时,编译器只为MyList<T>类型产生“泛型版”的IL代码——并不进行泛型的实例化,T在中间只充当占位符.例如:MyList 类型元数据中显示的<T> ...
- C# 篇基础知识9——特性、程序集和反射
特性(Attribute)是用于为程序元素添加额外信息的一种机制.比如记录文件修改时间或代码作者.提示某方法已经过期.描述如何序列化数据等等.方法.变量.属性.类.接口.结构体以及程序集等都是程序元素 ...
- c#基础语言编程-程序集和反射
程序集 什么是程序集? 1.程序集(assembly)是一个及一个以上托管模块,以及一些资源文件的逻辑组合. 2.程序集是组件复用,以及实施安全策略和版本策略的最小单位. 3.程序集是包含一个或者多个 ...
- C#编程之程序集和反射
这里我又唠叨几句,大家在学习的时候,如看书或者看视频时觉得非常爽,因为感觉基本都看得懂也都挺容易的,其实看懂是一回事,你自己会动手做出来是一回事,自己能够说出来又是另一回事了.应该把学到的东西变成自己 ...
- C#学习-程序集和反射
准备项目 1.新建一个空的解决方案MyProj.sln 2.在该解决方案下,建一个控制台项目P01.csproj 3.在该项目下,自己新建一个类MyFirstClass.cs 查看解决方案MyProj ...
- asp.net mvc本地程序集和GAC的程序集冲突解决方法
一个从asp.net mvc 3升级到asp.net mvc 4的项目发生了如下错误: [A]System.Web.WebPages.Razor.Configuration.HostSection c ...
- 教你50招提升ASP.NET性能(三):使用Microsoft的PDBs调试和分析外部的程序集和库
(3)Use Microsoft’s PDBs to debug or profile external assemblies or libraries 招数3: 使用Microsoft的PDBs调试 ...
- C#中的程序集和命名空间
C#中的程序集和命名空间 如果说命名空间是类库的逻辑组织形式,那么程序集就是类库的物理组织形式.只有同时指定类型所在的命名空间及实现该类型的程序集,才能完全限定该类型.<精通.NET核心技术-- ...
- .Net6新版本的AssemblyLoadContext 加载程序集和卸载程序集
准备俩个项目 第一个是控制台 第二个项目是类库 类库项目中只有一个示例class 将类库的代码生成dll 并且设置属性为复制到输出目录 using System.Runtime.Loader; var ...
随机推荐
- SQL点滴28—一个简单的存储过程
原文:SQL点滴28-一个简单的存储过程 在表中写入一条数据同事要向另外一个表中写入信息,所以会使用到事务.实际使用的时候还会一次向一个表中吸入多条数据,下面的存储过程,将字符串拆分成数组然后写入到表 ...
- 安装Visual Source Safe 2005 - 初学者系列 - 学习者系列文章
本文介绍微软的文档管理工具Visual Source Safe 2005的安装 从下列地址获取该工具: ed2k://|file|en_vss_2005.iso|108048384|C4BEC1EC3 ...
- 读书笔记—CLR via C#字符串及文本
前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可 ...
- [转载]LVS快速搭建教程
LVS配置教程 作者:oldjiang 一.前言 相信专程来读此文的读者对LVS必然有一定的了解,首先看图: 毋庸置疑,Load Balancer是负载调度器,由它将网络请求无缝隙调度到真实服务器,至 ...
- OpenGL Development Cookbook chapter7部分翻译
让我们通过以下简单步骤开始我们的配方: 1.通过读取外部的体数据文件,并通过该加载数据集数据转换成一个OpenGL纹理.也使硬件的mipmap生成.通常情况下,从使用一个横截面中获得的体积数据文件存储 ...
- HTML框架集之Frameset与Iframe简单应用
首先我们要明白此框架集的作用: 实现在一个页面中访问多个html页面的目的Frameset的用法: //定义框架集<frameset><frame src="demo.ht ...
- Nutch搜索引擎Solr简介及安装
Nutch搜索引擎(第2期)_ Solr简介及安装 1.Solr简介 Solr是一个高性能,采用Java5开发,基于Lucene的全文搜索服务器.同时对其进行了扩展,提供了比Lucene更为丰富的 ...
- ASP.NET MVC应用程序实现下载功能
ASP.NET MVC应用程序实现下载功能 上次Insus.NET有在MVC应用程序实现了上传文件的功能<MVC应用程序显示上传的图片> http://www.cnblogs.com/in ...
- ACE编译运行错误解决
使用VS2010编译运行ACE版本号为6.0.1 按照网上步骤添加config.h,在ACE_wrappers/ace目录下 ,新建 config.h 文件加入以下内容: #include " ...
- iOS基础 - Quartz 2D绘图的基本步骤
一.使用Quartz 2D绘图的基本步骤 1) 获取上下文context(绘制图形的地方) 2) 设置路径(路径是用来描述形状的) 3) 将路径添加到上下文 4) 设置上下文属性(设置颜色,线宽, ...