程序集和反光(C#)

那边自个儿又唠叨几句,大家在就学的时候,如看书恐怕看视频时觉得那么些爽,因为觉得基本都看得懂也都挺不难的,其实看懂是两回事,你协调会下手做出来是一次事,自身力所能及说出去又是另一次事了。应该把学到的事物变为本身的东西,而不是依样画瓢。

在说反射此前,我们先来询问一下怎么是程序集?

程序集

程序集是.net中的概念,程序集可以视作是给一堆相关类打二个包,约等于java中的jar包。

先后集带有:

  • 财富文件
  • 项目元数据(描述在代码中定义的每一品类和成员,二进制方式)
  • IL代码(这个都被封装在exe或dll中)

exe与dll的区别。

exe可以运作,dll不可以直接运转,因为exe中有三个main函数(入口函数)。

类型元数据那个音讯方可经过AssemblyInfo.cs文件来自定义。在每2个.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")]

这个新闻在哪个地方显示吗?就在我们先后集的习性当中进行反映

图片 1

俺们一贯在装置一些CS客户端程序的时候,在设置目录上边会看见许多的程序集文件。

接纳程序集的益处

  • 程序中只援引必须的程序集,减小程序的尺码。
  • 先后集可以打包一些代码,只提供须要的拜访接口。
  • 方便扩充。

哪些添加程序集的引用?

直接添加程序集路径大概添加消除方案中的项目引用。

当我们须求增添一个程序的时候,你大概会一贯在原来的序列中进行添加,那那样的话,假如你的那几个代码想共享给人家接纳啊?你就足以打包成3个程序集,然后旁人只要经过引用你这些顺序集就足以展开增添了。像大家广大的.net第壹方框架库,如log4net、unity等等。

小心:无法添加循环引用

什么样是添加循环引用?就是说A项目只要添加了B项目的花色引用,那么此时B项目无法再添加A项目标体系引用,相当于说添加项目引用时,必须是单向的,像大家广大的三层框架之间的类型引用。

反射

有关反射,你一旦是做.net开发,你就肯定每二十三十一日在用。因为VS的智能提示就是经过使用了反光技术来贯彻的,还有我们常用的反编译神器Reflector.exe,看它的名字就了然了。项目中相比较普遍的,是因而整合配置文件来动态实例化对象,如切换数据库实例,或许Sprint.net的通过布置文件来贯彻依靠注入等。

反射技术其实就是动态获取程序集的元数据的效应,反射通过动态加载dll,然后对其举办分析,从而创设对象,调用成员。

Type是对类的叙说,Type类是促成反射的2个关键的类,通过它大家可以博得类中的全数消息,包涵方法、属性等。可以动态调用类的天性、方法。

反射的产出让创制对象的措施暴发了改观,因为过去面完创立对象都以一向通过new。

dll里面有两部分东西:IL中间语言和metadate成分据。

在.NET中反射用到命名空间是System.Reflection,那里笔者先经过1个德姆o来看反射能做些什么

① 、  新建控制台项目Reflected德姆o

贰 、  新建类库项目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
    {
    }
}

叁 、  项目Reflected德姆o,添加My.Sqlserver.Dal的门类引用,作者如此做的目标是为着有利于项目Reflected德姆o中的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);
        }
    }
}

图片 2

动态创立对象

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

她俩之间的区分
ass.CreateInstance(string typeName)
会动态调用类的无参构造函数创建三个目标,重临值就是创办的对象,倘诺没有无参构造函数就会报错。
图片 3

            Assembly assembly = Assembly.Load("My.Sqlserver.Dal");
            object obj = assembly.CreateInstance("My.Sqlserver.Dal.SqlServerHelper");
            Console.WriteLine(obj.GetType().ToString());

图片 4

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

       public SqlServerHelper(int age)
        {
            this.age = age;
        }

本条时候再来运转创制实例的代码就会报错了,而编译时是不报错的。

图片 5

于是大家一般推荐使用Activator.CreateInstance方法来创制反射对象,因为此措施有很多重载,辅助将参数传递给构造函数。

 图片 6

那会儿再调用就不会出现分外了。

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

图片 7

            SqlServerHelper _SqlServerHelper = new SqlServerHelper(1);
            bool result = typeof(SqlServerHelper).IsInstanceOfType(_SqlServerHelper);
            Console.WriteLine(result);

图片 8

            SqlServerHelper _SqlServerHelper = new SqlServerHelper(1);
            bool result = typeof(SqlServerHelper).IsSubclassOf(typeof(BaseSql));
            Console.WriteLine(result);

图片 9

项目中常用的运用反射来动态切换数据库德姆o:

新建类库项目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>

Reflected德姆o项目中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());

图片 10

这么每回要求切换数据库时,只要修改配置文件就足以了。

花色布局:

图片 11

小心:反射固然很有力,但却是比较耗质量的,所以一般和缓存结合起来使用。

花色源码:ReflectedDemo.zip

相关文章