服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C# 程序集和反射详解

C# 程序集和反射详解

2021-12-18 15:35邹琼俊 C#

本文主要介绍了C# 程序集和反射的相关知识。具有一定的参考价值,下面跟着小编一起来看下吧

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

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

程序集

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

程序集包含:

  • 资源文件
  • 类型元数据(描述在代码中定义的每一类型和成员,二进制形式)
  • il代码(这些都被封装在exe或dll中)

exe与dll的区别。

exe可以运行,dll不能直接运行,因为exe中有一个main函数(入口函数)。

类型元数据这些信息可以通过assemblyinfo.cs文件来自定义。在每一个.net项目中都存在一个assemblyinfo.cs文件,代码格式:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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,前者为共有类,后者为私有类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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程序集。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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# 程序集和反射详解

?
1
2
3
assembly assembly = assembly.load("my.sqlserver.dal");
object obj = assembly.createinstance("my.sqlserver.dal.sqlserverhelper");
console.writeline(obj.gettype().tostring());

C# 程序集和反射详解

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

?
1
2
3
4
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

然后查看调用代码:

?
1
2
bool result = typeof(basesql).isassignablefrom(typeof(sqlserverhelper));
console.writeline(result);

C# 程序集和反射详解

?
1
2
3
sqlserverhelper _sqlserverhelper = new sqlserverhelper(1);
bool result = typeof(sqlserverhelper).isinstanceoftype(_sqlserverhelper);
console.writeline(result);

C# 程序集和反射详解

?
1
2
3
sqlserverhelper _sqlserverhelper = new sqlserverhelper(1);
bool result = typeof(sqlserverhelper).issubclassof(typeof(basesql));
console.writeline(result);

C# 程序集和反射详解

项目中常用的利用反射来动态切换数据库demo:

新建类库项目my.sql.idal,并添加接口isqlhelper。通过接口来实现数据库操作的类的解耦,因为接口是抽象的。

?
1
2
3
4
public interface isqlhelper
{
  string query();
}

添加类库项目my.mysql.dal,并新增类mysqlhelper.cs

my.sqlserver.dal、my.mysql.dal项目分别添加对项目my.sql.idal的引用。让sqlserverhelper继承自接口isqlhelper

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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配置项

?
1
2
3
<appsettings>
 <add key="dbname" value="my.sqlserver.dal,sqlserverhelper"/>
</appsettings>

reflecteddemo项目中program.cs调用代码:

?
1
2
3
4
5
6
7
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://www.cnblogs.com/jiekzou/p/6285082.html

延伸 · 阅读

精彩推荐
  • C#如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    这篇文章主要给大家介绍了关于如何使用C#将Tensorflow训练的.pb文件用在生产环境的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴...

    bbird201811792022-03-05
  • C#C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    这篇文章主要介绍了C#设计模式之Strategy策略模式解决007大破密码危机问题,简单描述了策略模式的定义并结合加密解密算法实例分析了C#策略模式的具体使用...

    GhostRider10972022-01-21
  • C#C#微信公众号与订阅号接口开发示例代码

    C#微信公众号与订阅号接口开发示例代码

    这篇文章主要介绍了C#微信公众号与订阅号接口开发示例代码,结合实例形式简单分析了C#针对微信接口的调用与处理技巧,需要的朋友可以参考下...

    smartsmile20127762021-11-25
  • C#SQLite在C#中的安装与操作技巧

    SQLite在C#中的安装与操作技巧

    SQLite,是一款轻型的数据库,用于本地的数据储存。其优点有很多,下面通过本文给大家介绍SQLite在C#中的安装与操作技巧,感兴趣的的朋友参考下吧...

    蓝曈魅11162022-01-20
  • C#VS2012 程序打包部署图文详解

    VS2012 程序打包部署图文详解

    VS2012虽然没有集成打包工具,但它为我们提供了下载的端口,需要我们手动安装一个插件InstallShield。网上有很多第三方的打包工具,但为什么偏要使用微软...

    张信秀7712021-12-15
  • C#利用C#实现网络爬虫

    利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...

    C#教程网11852021-11-16
  • C#三十分钟快速掌握C# 6.0知识点

    三十分钟快速掌握C# 6.0知识点

    这篇文章主要介绍了C# 6.0的相关知识点,文中介绍的非常详细,通过这篇文字可以让大家在三十分钟内快速的掌握C# 6.0,需要的朋友可以参考借鉴,下面来...

    雨夜潇湘8272021-12-28
  • C#深入理解C#的数组

    深入理解C#的数组

    本篇文章主要介绍了C#的数组,数组是一种数据结构,详细的介绍了数组的声明和访问等,有兴趣的可以了解一下。...

    佳园9492021-12-10