详解c# Emit技术

 更新时间:2020年12月21日 09:47:11   作者:Min.Xiaoshuang  
这篇文章主要介绍了c# Emit技术的相关资料,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下

我们常常有一个应用场景,由我们的C#代码,动态生成一个EXE,其应用场景可以非常多,比如软件授权,可以输入授权信息后,生成一个授权的DLL等,那如何实现这个功能呢,就要提到一个技术Emit。

1、Emit概述

Emit,可以称为发出或者产生。在Framework中,与Emit相关的类基本都存在于System.Reflection.Emit命名
空间下。可见Emit是作为反射的一个元素存在的。说道反射,大家应该都不陌生,它允许我们查看程序集的元素据,从而取得形如程序集包含哪些类型,类型包
含哪些方法等等大量的信息。但是反射也仅能够‘看',而Emit则可以在运行时动态生成代码。接下来就来看看如何用Emit生成代码。

2、程序集(Assembly)和模块(Managed Module)

程序集是一个或多个模块、资源文件的逻辑性分组,其次程序集是重用,安全性和版本控制的最小单元。我们所见到的DLL、EXE都可以称为一个Assembly,一个Assembly里面包含多个Module,不过通常,我们VS编译的时候,会只编译一个Module,假如在一个Assembly中要编译多个Module,则要借助csc.exe实现。

3、动态生成代码操作

定义程序集

 //定义一个程序集的名称
 var asmName = new AssemblyName("MyClass");

//首先就需要定义一个程序集
 var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);

定义模块,和指定程序集的保存名称

//定义一个构建类
var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule", "MyAssembly.dll");

定义一个类 和方法

//定义一个类
 var defClassBuilder =defModuleBuilder.DefineType("MyClass", TypeAttributes.Public);
 //定义一个方法
var methodBldr = defClassBuilder.DefineMethod("MyMethod",
  MethodAttributes.Public,
   null,//返回类型
   null//参数的类型
 );

以上通过创建,已经确定了程序集和模块,也定义了当前模块中的一个类和方法,但这个类的MyMethod方法只定义了一个声明,并没有定义实体操作,以下就需要应用到Emit技术中一个技术OpCode。

OpCode 是描述中间语言 (IL) 指令。这个指令非常多,可以查看微软官网:https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.emit.opcodes?view=netframework-4.8

通过OpCode我们可以定义方法的内容如下:

//获取IL生成器
      var il = defMethodBuilder.GetILGenerator();
      //定义一个字符串
      il.Emit(OpCodes.Ldstr, "生成的第一个程序");
      //调用一个函数
      il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
      //返回到方法开始(返回)
      il.Emit(OpCodes.Ret);

通过以上的定义,我们完成了一个程序集、模块、类和方法的定义,我们怎么把以上的定义的信息进行创建和保存,需要调用以下函数:

    //创建类型
      defClassBuilder.CreateType();

      //保存程序集
      defAssembly.Save("MyAssemblydll");

我们可以在运行程序看到如下效果:

以下通过创建程序集,并且调用的代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
  class Program
  {
    static void Main(string[] args)
    {

      CreateAssembly();
      LoadAssembly();

      Console.ReadKey();
    }

    public static void LoadAssembly()
    {
      var ass = AppDomain.CurrentDomain.Load("MyAssembly");
      var m = ass.GetModule("MyModule");
      var ts = m.GetTypes();
      var t = ts.FirstOrDefault();
      if (t != null)
      {
        object obj = Activator.CreateInstance(t);
        var me = t.GetMethod("MyMethod");
        me.Invoke(obj, null);
      }
    }
    public static void CreateAssembly()
    {


      //定义一个程序集的名称
      var asmName = new AssemblyName("MyAssembly");

      //首先就需要定义一个程序集
      var defAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
      //定义一个构建类
      var defModuleBuilder = defAssembly.DefineDynamicModule("MyModule", "MyAssembly.dll");


      //定义一个类
      var defClassBuilder = defModuleBuilder.DefineType("MyClass", TypeAttributes.Public);

      //定义一个方法
      var defMethodBuilder = defClassBuilder.DefineMethod("MyMethod",
        MethodAttributes.Public,
        null,//返回类型
        null//参数类型
        );
      //获取IL生成器
      var il = defMethodBuilder.GetILGenerator();
      //定义一个字符串
      il.Emit(OpCodes.Ldstr, "生成的第一个程序");
      //调用一个函数
      il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
      //返回到方法开始(返回)
      il.Emit(OpCodes.Ret);

      //创建类型
      defClassBuilder.CreateType();

      //保存程序集
      defAssembly.Save("MyAssembly.dll");
    }

  }
}

显示效果如下:

以上就是详解c# Emit技术的详细内容,更多关于c# Emit技术的资料请关注脚本之家其它相关文章!

相关文章

  • C#实现Zip压缩目录中所有文件的方法

    C#实现Zip压缩目录中所有文件的方法

    这篇文章主要介绍了C#实现Zip压缩目录中所有文件的方法,涉及C#针对文件的读写与zip压缩相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-07-07
  • c# 两个数组比较,将重复部分去掉,返回不重复部分的实现

    c# 两个数组比较,将重复部分去掉,返回不重复部分的实现

    下面小编就为大家带来一篇c# 两个数组比较,将重复部分去掉,返回不重复部分的实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • C#语法相比其它语言比较独特的地方(三)

    C#语法相比其它语言比较独特的地方(三)

    这篇文章主要介绍了C#语法相比其它语言比较独特的地方(三),本文讲解了在C++中允许从一个case贯穿到另一个case标签、as和is只会检测待转化类型的类型,而不会进行其它操作等内容,需要的朋友可以参考下
    2015-04-04
  • C#调用AForge实现摄像头录像的示例代码

    C#调用AForge实现摄像头录像的示例代码

    这篇文章主要介绍了C#调用AForge实现摄像头录像的示例代码,非常具有实用价值,需要的朋友可以参考下
    2017-09-09
  • C#连接ODBC数据源的方法

    C#连接ODBC数据源的方法

    这篇文章主要介绍了C#连接ODBC数据源的方法,实例分析了C#连接ODBC操作数据库的技巧,非常具有实用价值,需要的朋友可以参考下
    2015-04-04
  • C# 如何获取处于运行中的Excel、Word对象

    C# 如何获取处于运行中的Excel、Word对象

    这篇文章主要介绍了C# 获取处于运行中的Excel、Word对象操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • Unity实现图片轮播组件

    Unity实现图片轮播组件

    这篇文章主要为大家详细介绍了Unity实现图片轮播组件的相关方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • C# SortedList排序列表的实现

    C# SortedList排序列表的实现

    本文主要介绍了C# SortedList排序列表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • C#创建dll类库的图文步骤

    C#创建dll类库的图文步骤

    类库让我们的代码可复用,我们只需要在类库中声明变量一次,就能在接下来的过程中无数次地使用,而无需在每次使用前都要声明它。这样一来,就节省了我们的内存空间,需要的朋友可以参考下
    2017-01-01
  • C#实现启用与禁用本地网络的方式小结【3种方式】

    C#实现启用与禁用本地网络的方式小结【3种方式】

    这篇文章主要介绍了C#实现启用与禁用本地网络的方式,结合实例形式总结分析了使用Hnetcfg.dll、Shell32.dll及setupapi.dll三种启用与禁用本地网络的操作方法,需要的朋友可以参考下
    2016-07-07

最新评论