• 欢迎访问我爱CSharp学习网,这里有最新最全的C#书籍,C#视频。
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏我爱C#学习网吧
  • 推荐使用最新版Chrome浏览器和火狐浏览器访问本网站

C#编程之程序集和反射

C#杂烩 52csharp 1864次浏览 0个评论 扫描二维码

(点击上方蓝字,可快速关注我们)


来源:邹琼俊

cnblogs.com/jiekzou/p/6285082.html


这里我又唠叨几句,大家在学习的时候,如看书或者看视频时觉得非常爽,因为感觉基本都看得懂也都挺容易的,其实看懂是一回事,你自己会动手做出来是一回事,自己能够说出来又是另一回事了。应该把学到的东西变成自己的东西,而不是依样画瓢。


在说反射之前,我们先来了解一下什么是程序集?


程序集


程序集是.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”)]


这些信息在哪里体现呢?就在我们程序集的属性当中进行体现


C#编程之程序集和反射


我们平时在安装一些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);

        }

    }

}


C#编程之程序集和反射


动态创建对象


通过ass.CreateInstance(string typeName) 和Activator.CreateInstance(Type t)方法


他们之间的区别


ass.CreateInstance(string typeName) 会动态调用类的无参构造函数创建一个对象,返回值就是创建的对象,如果没有无参构造函数就会报错。


C#编程之程序集和反射


Assembly assembly = Assembly.Load(“My.Sqlserver.Dal”);

object obj = assembly.CreateInstance(“My.Sqlserver.Dal.SqlServerHelper”);

Console.WriteLine(obj.GetType().ToString());


C#编程之程序集和反射

如果我们来修改SqlServerHelper类的代码,添加如下构造函数:


public SqlServerHelper(int age)

{

     this.age = age;

}


这个时候再来运行创建实例的代码就会报错了,而编译时是不报错的。


C#编程之程序集和反射


所以我们一般推荐使用Activator.CreateInstance方法来创建反射对象,因为此方法有许多重载,支持将参数传递给构造函数。


C#编程之程序集和反射


此时再调用就不会出现异常了。


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);


C#编程之程序集和反射


SqlServerHelper _SqlServerHelper = new SqlServerHelper(1);

bool result = typeof(SqlServerHelper).IsInstanceOfType(_SqlServerHelper);

Console.WriteLine(result);


C#编程之程序集和反射


SqlServerHelper _SqlServerHelper = new SqlServerHelper(1);

bool result = typeof(SqlServerHelper).IsSubclassOf(typeof(BaseSql));

Console.WriteLine(result);


C#编程之程序集和反射


项目中常用的利用反射来动态切换数据库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#编程之程序集和反射


这样每次需要切换数据库时,只要修改配置文件就可以了。


项目结构:


C#编程之程序集和反射


注意:反射虽然很强大,但却是比较耗性能的,所以一般和缓存结合起来使用。


项目源码:ReflectedDemo.zip (http://pan.baidu.com/s/1mioFwSg)


 

C#编程之程序集和反射


我爱CSharp学习网 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明C#编程之程序集和反射
喜欢 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址