Home >Backend Development >C#.Net Tutorial >Detailed explanation of graphic code of C# assembly and reflection

Detailed explanation of graphic code of C# assembly and reflection

黄舟
黄舟Original
2017-03-29 11:45:381904browse

This article mainly introduces the related knowledge of C# assembly and reflection. It has a certain reference value. Let’s take a look at it with the editor.

Here I will say a few more words. Everyone feels very happy when they are studying, such as reading books or watching videos, because they basically feel like watching It's quite easy to understand. In fact, it's one thing to understand. It's another thing to be able to do it yourself. It's another thing to be able to speak it yourself. You should make what you learn into your own, rather than copying others.

Before talking about reflection, let’s first understand what an assembly is?

Assembly

Assembly is a concept in .net. An assembly can be seen as a package for a bunch of related classes, which is equivalent to jar in java. Bag.

Assemblies contain:

  • Resource files

  • Type metadata (describing each type and Members, binary form)

  • IL code (these are encapsulated in exe or dll)

The difference between exe and dll .

exe can run, but dll cannot run directly because there is a mainfunction (entry function) in exe.

Type metadata This information can be customized through the AssemblyInfo.cs file. There is an AssemblyInfo.cs file in every .net project. The code format is:

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")]

Where is this information reflected? It is reflected in the properties of our assembly

When we usually install some CS client programs, we will see many under the installation directory assembly file.

Benefits of using assemblies

  • #Only the necessary assemblies are referenced in the program, reducing the size of the program.

  • Assemblies can encapsulate some code and provide only the necessary access interfaces.

  • Convenient expansion.

How to add a reference to an assembly?

Add the assembly path directly or add a project reference in the solution.

When we need to extend a program, you may add it directly to the original project. In this case, what if you want to share your code with others? You can package it into an assembly, and then others can extend it by referencing your assembly. Like our common .net third-party framework libraries, such as log4net, unity, etc.

Note: You cannot add a cyclic referencereference

What is adding a circular reference? That is to say, if project A adds a project reference to project B, then project B cannot add a project reference to project A at this time. That is to say, when adding a project reference, it must be one-way, like our common three-tier framework. project references.

Reflection

Regarding reflection, as long as you are doing .net development, you must use it every day. Because the intelligent prompts of VS are realized by applying reflection technology, as well as our commonly used decompilation artifact Reflector.exe, just look at its name. It is more common in projects to dynamically instantiate objects by combining configuration files, such as switching database instances, or Sprint.net's dependency injection through configuration files.

Reflection technology is actually the function of dynamically obtaining the metadata of an assembly. Reflection dynamically loads the dll and then parses it to create objects and call members.

Type is a description of the class. The Type class is an important class for implementing reflection. Through it, we can obtain all the information in the class, including methods, attributes, etc. Properties and methods of classes can be called dynamically.

The emergence of reflection has changed the way of creating objects, because in the past, objects were created directly through new.

There are two parts in the dll: IL intermediate language and metadate element data.

Namespace used in .NET reflection is System.Reflection. Here I will first use a Demo to see what reflection can do

1. Create a new console Project ReflectedDemo

2. NewClass LibraryProject My.Sqlserver.Dal

Create two new classes, SqlServerHelper and SqlCmd, the former is a public class and the latter is a private class

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. Project ReflectedDemo, add the project reference of My.Sqlserver.Dal. The purpose of this is to facilitate the existence of the My.Sqlserver.Dal.dll assembly in the bin directory in the project ReflectedDemo.

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

Dynamic creation of objects

通过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(&#39;,&#39;)[0];
      string strClass=str.Split(&#39;,&#39;)[1];
      Assembly assembly = Assembly.Load(strAssembly);
      Type t = assembly.GetType(strAssembly + "." + strClass);
      ISqlHelper obj = Activator.CreateInstance(t) as ISqlHelper;
      Console.WriteLine(obj.Query());

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

项目结构:

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

The above is the detailed content of Detailed explanation of graphic code of C# assembly and reflection. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn