这里我又絮聒几句,人人在进修的时候,如看书或许看视频时以为异常爽,由于觉得基础都看得懂也都挺轻易的,实在看懂是一回事,你本身会动手做出来是一回事,本身能够说出来又是另一回事了。应当把学到的东西变成本身的东西,而不是依样画瓢。
在说反射之前,我们先来相识一下什么是顺序集?
顺序集
顺序集是.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 = 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的子类
Type类中另有一个IsAbstract属性:推断是不是为笼统的,包含接口。
它们经常使用的原因是我们经由过程反射能够取到的东西太多了,我们须要对数据举行过滤。
增加类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.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 = 16; 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(',')[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());
如许每次须要切换数据库时,只需修正设置文件就能够了。
项目构造:
注重:反射虽然很壮大,但倒是比较耗机能的,所以平常和缓存连系起来运用。
以上就是C#顺序集和反射的图文代码详解的细致内容,更多请关注ki4网别的相干文章!