`

c# - reflection in the System.Reflection.Emit namespace - AssemblyBuilder

    博客分类:
  • C#
c# 
阅读更多

in the previous example, we have discussed the ModuleBuilder, There is a slightly related class that you can use to generate dynamic code. 

 

In this code, we are going to show how to create some assemblies which you can both run and save to a file and load execute again.

 

 

the referenced material is AssemblyBuilder class. First and foremost, let's see the code. 

 

 

class DemoAssemblyBuilder
  {
    internal static void Main(string[] args)
    {
      // An assembly consists of one or more modules, each of which 
      // contains zero or more types. This code creates a single-module 
      // assembly, the most common case. The module contains one type, 
      // named "MyDynamicType", that has a private field, a property 
      // that gets and sets the private field, constructors that  
      // initialize the private field, and a method that multiplies  
      // a user-supplied number by the private field value and returns 
      // the result. In C# the type might look like this: 
      /*
      public class MyDynamicType
      {
          private int m_number;

          public MyDynamicType() : this(42) {}
          public MyDynamicType(int initNumber)
          {
              m_number = initNumber;
          }

          public int Number
          {
              get { return m_number; }
              set { m_number = value; }
          }

          public int MyMethod(int multiplier)
          {
              return m_number * multiplier;
          }
      }
      */


      AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
      AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(
        aName,
        AssemblyBuilderAccess.RunAndSave); // the generated asssembly will both be used to run and saved as some Assembly file for later use
                                           // other values include AssemblyBuilderAccess.Run, AssemblyBuilderAccess.Save, AssemblyBuilderAccess.ReflectionOnly, AssemblyBuilderAccess.RunAndCollect

      // for a single-module assembly, the module name is usually
      // the assembly name plus an extension
      ModuleBuilder mb = ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");  // since we are going to save, we have to give it a name

      TypeBuilder tb = mb.DefineType(
        "MyDynamicType",
        TypeAttributes.Public);

      // Add a private field of type int (Int32)
      FieldBuilder fbNumber = tb.DefineField(
        "m_number",
        typeof(int),
        FieldAttributes.Private);                            // private int m_number 


      // Define a constructor that takes an integer argument and 
      // stores it in the private field.
      Type[] parameterTypes = { typeof(int) };
      ConstructorBuilder ctor1 = tb.DefineConstructor(
        MethodAttributes.Public,
        CallingConventions.Standard,
        parameterTypes);

      ILGenerator ctor1IL = ctor1.GetILGenerator();

      // For a constructor, argument zero is a reference to the new 
      // instance. Push it on the stack before calling the base 
      // class constructor. Specify the default constructor of the  
      // base class (System.Object) by passing an empty array of  
      // types (Type.EmptyTypes) to GetConstructor.
      ctor1IL.Emit(OpCodes.Ldarg_0);                                 // for a class method, the first OpCodes.Ldarg_0 is always necessary because it is the 'this' pointer
      ctor1IL.Emit(OpCodes.Ldarg_1);
      ctor1IL.Emit(OpCodes.Stfld, fbNumber);
      ctor1IL.Emit(OpCodes.Ret);

      // Define a default constructor that supplies a default value 
      // for the private field. For parameter types, pass the empty 
      // array of types or pass null.
      ConstructorBuilder ctor0 = tb.DefineConstructor(
        MethodAttributes.Public,
        CallingConventions.Standard,
        Type.EmptyTypes);

      ILGenerator ctor0IL = ctor0.GetILGenerator();
      // For a constructor, argument zero is a reference to the new 
      // instance. Push it on the stack before pushing the default 
      // value on the stack, then call constructor ctor1.
      ctor0IL.Emit(OpCodes.Ldarg_0);
      ctor0IL.Emit(OpCodes.Ldc_I4_S, 42);                       // Ldc stands for load constant as Int 4 bytes (short form) onto the stack
      ctor0IL.Emit(OpCodes.Call, ctor1);                        // this: this(42)
      ctor0IL.Emit(OpCodes.Ret);                                // return


      // Define a property named Number that gets and sets the private  
      // field. 
      // 
      // The last argument of DefineProperty is null, because the 
      // property has no parameters. (If you don't specify null, you must 
      // specify an array of Type objects. For a parameterless property, 
      // use the built-in array with no elements: Type.EmptyTypes)
      PropertyBuilder pbNumber = tb.DefineProperty(
        "Number",
        PropertyAttributes.HasDefault,        // has default, what is the default value? 
        typeof(int),                          // type is int
        null
        );

      // The property "set" and property "get" methods require a special
      // set of attributes.
      MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig; // we have the following attribute because we want to assign setter/getter to the code


      // Define the "get" accessor method for Number. The method returns
      // an integer and has no arguments. (Note that null could be  
      // used instead of Types.EmptyTypes)
      MethodBuilder mbNumberGetAccessor = tb.DefineMethod(
        "get_Number",           // MethodAttributes.SpecialName
        getSetAttr,
        typeof(int),
        Type.EmptyTypes);       // get_Number(void) 

      ILGenerator numberGetIL = mbNumberGetAccessor.GetILGenerator();

      // For an instance property, argument zero is the instance. Load the  
      // instance, then load the private field and return, leaving the 
      // field value on the stack.
      numberGetIL.Emit(OpCodes.Ldarg_0);
      numberGetIL.Emit(OpCodes.Ldfld, fbNumber); // register  <- addressof fbNumber;
      numberGetIL.Emit(OpCodes.Ret);



      // Define the "set" accessor method for Number, which has no return 
      // type and takes one argument of type int (Int32).
      MethodBuilder mbNumberSetAccessor = tb.DefineMethod(
        "set_Number",
        getSetAttr,
        null,                   // void set_Number(int value)
        new Type[] { typeof(int) });

      ILGenerator numberSetIL = mbNumberSetAccessor.GetILGenerator();
      // Load the instance and then the numeric argument, then store the 
      // argument in the field.
      numberSetIL.Emit(OpCodes.Ldarg_0);
      numberSetIL.Emit(OpCodes.Ldarg_1);
      numberSetIL.Emit(OpCodes.Stfld, fbNumber);
      numberSetIL.Emit(OpCodes.Ret);


      // Last, map the "get" and "set" accessor methods to the 
      // PropertyBuilder. The property is now complete. 
      pbNumber.SetGetMethod(mbNumberGetAccessor);
      pbNumber.SetSetMethod(mbNumberSetAccessor);

      // Define a method that accepts an integer argument and returns 
      // the product of that integer and the private field m_number. This 
      // time, the array of parameter types is created on the fly.
      MethodBuilder meth = tb.DefineMethod(
        "MyMethod",
        MethodAttributes.Public,
        typeof(int),
        new Type[] { typeof(int) });

      ILGenerator methIL = meth.GetILGenerator();
      // To retrieve the private instance field, load the instance it 
      // belongs to (argument zero). After loading the field, load the  
      // argument one and then multiply. Return from the method with  
      // the return value (the product of the two numbers) on the  
      // execution stack.
      methIL.Emit(OpCodes.Ldarg_0);          // it is the caller that push the argument to the stack, so in the function body , we assume that we already have the parameters on stack
      methIL.Emit(OpCodes.Ldfld, fbNumber);  // the OpCodes.Ldflda is to load the address of the field
      methIL.Emit(OpCodes.Ldarg_1);
      methIL.Emit(OpCodes.Mul);
      methIL.Emit(OpCodes.Ret);

      // Finish the type.
      Type t = tb.CreateType();

      // The following line saves the single-module assembly. This 
      // requires AssemblyBuilderAccess to include Save. You can now 
      // type "ildasm MyDynamicAsm.dll" at the command prompt, and 
      // examine the assembly. You can also write a program that has 
      // a reference to the assembly, and use the MyDynamicType type. 
      // 
      ab.Save(aName.Name + ".dll");

      // Because AssemblyBuilderAccess includes Run, the code can be 
      // executed immediately. Start by getting reflection objects for 
      // the method and the property.
      MethodInfo mi = t.GetMethod("MyMethod");
      PropertyInfo pi = t.GetProperty("Number");

      // Create an instance of MyDynamicType using the default 
      // display it again. Use null to indicate the property 
      // has no index
      object o1 = Activator.CreateInstance(t);

      // Display the value of the property, then change it to 127 and  
      // display it again. Use null to indicate that the property 
      Console.WriteLine("o1.Number: {0}", pi.GetValue(o1, null));
      pi.SetValue(o1, 127, null);
      Console.WriteLine("o1.Number: {0}", pi.GetValue(o1, null));
      // has no index.

      // Call MyMethod, passing 22, and display the return value, 22 
      // times 127. Arguments must be passed as an array, even when 
      // there is only one. 
      object[] arguments = { 22 };
      Console.WriteLine("o1.MyMethod(22): {0} ", mi.Invoke(o1, arguments));

      // Create an instance of MyDynamicType using the constructor 
      // that specifies m_Number. The constructor is identified by 
      // matching the types in the argument array. In this case,  
      // the argument array is created on the fly. Display the  
      // property value. 
      object o2 = Activator.CreateInstance(t, new object[] { 5280 });
      Console.WriteLine("o2.Number : {0}", pi.GetValue(o2, null));
    }
  }

 

 

 

so, some summary that we can get from the code above:

 

 

  • you can generate some .dll on the fly, while you can also save the .dll to a local file. it require high level of persmission to do that operation.
  • To declare a Property, you first create a property builder, initialize a special MethodAttributes, and then with MethodBuilder, defines two method, get_Property, and set_Property, bind them to the Property with SetGetMethod and SetSetMethod()...
  • You will see a lot of raw IL code, which may/may not have the portable issues. 

 

If you take a look at the generated code then you might be able to find this.

 

 

//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.0.30319.1




// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly DynamicAssemblyExample
{
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.module DynamicAssemblyExample
// MVID: {2F8EC134-454A-4A25-BDB6-53649D2F2973}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x00410000


// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi MyDynamicType
       extends [mscorlib]System.Object
{
  .field private int32 m_number
  .method public specialname rtspecialname 
          instance void  .ctor(int32 A_1) cil managed
  {
    // 代码大小       8 (0x8)
    .maxstack  2
    IL_0000:  ldarg.0
    IL_0001:  ldarg.1
    IL_0002:  stfld      int32 MyDynamicType::m_number
    IL_0007:  ret
  } // end of method MyDynamicType::.ctor

  .method public specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // 代码大小       12 (0xc)
    .maxstack  2
    IL_0000:  ldarg.0
    IL_0001:  ldc.i4.s   42
    IL_0003:  nop
    IL_0004:  nop
    IL_0005:  nop
    IL_0006:  call       instance void MyDynamicType::.ctor(int32)
    IL_000b:  ret
  } // end of method MyDynamicType::.ctor

  .method public hidebysig specialname instance int32 
          get_Number() cil managed
  {
    // 代码大小       7 (0x7)
    .maxstack  1
    IL_0000:  ldarg.0
    IL_0001:  ldfld      int32 MyDynamicType::m_number
    IL_0006:  ret
  } // end of method MyDynamicType::get_Number

  .method public hidebysig specialname instance void 
          set_Number(int32 A_1) cil managed
  {
    // 代码大小       8 (0x8)
    .maxstack  2
    IL_0000:  ldarg.0
    IL_0001:  ldarg.1
    IL_0002:  stfld      int32 MyDynamicType::m_number
    IL_0007:  ret
  } // end of method MyDynamicType::set_Number

  .method public instance int32  MyMethod(int32 A_1) cil managed
  {
    // 代码大小       9 (0x9)
    .maxstack  2
    IL_0000:  ldarg.0
    IL_0001:  ldfld      int32 MyDynamicType::m_number
    IL_0006:  ldarg.1
    IL_0007:  mul
    IL_0008:  ret
  } // end of method MyDynamicType::MyMethod

  .property int32 Number()
  {
    .get instance int32 MyDynamicType::get_Number()
    .set instance void MyDynamicType::set_Number(int32)
  } // end of property MyDynamicType::Number
} // end of class MyDynamicType


// =============================================================

// *********** 反汇编完成 ***********************
 

 

 

分享到:
评论

相关推荐

    最新nodejs安装包 v8.12.0-linux-x64.tar.xz

    eventEmitter.emit('data_received'); } // 绑定 connection 事件处理程序 eventEmitter.on('connection', connectHandler); // 触发 connection 事件 eventEmitter.emit('connection'); console.log("程序...

    Managed.Reflection, System.Reflection [.Emit ]的托管替换.zip

    Managed.Reflection, System.Reflection [.Emit ]的托管替换 Managed.ReflectionManaged.Reflection 是对 System.Reflection 和 System.Reflection.Emit的完全管理的替换。 System.Reflection 不同,它不绑定到

    iperf-3.9-win64.zip

    --timestamps &lt;format&gt; emit a timestamp at the start of each output line (using optional format string as per strftime(3)) -d, --debug emit debugging output -v, --version show version information ...

    PyPI 官网下载 | fluent_logger-0.10.0-py2.py3-none-any.whl

    4. 记录日志:调用logger的`emit`方法,传递日志数据,例如`logger.emit('logtag', {'key': 'value'})`。 fluent_logger库的一大特点是支持结构化的日志数据,这使得日志数据更加易读和分析。此外,它还提供了异步...

    Vue.js-2.0-参考手册.CHM

    包括组件定义、props传递、事件通信($emit和$v-on)、自定义指令、插槽等内容。 3. **状态管理**:Vue 2.0引入了Vuex作为官方推荐的状态管理工具,用于集中管理组件间的共享状态。学习Vuex的基本原理、状态、动作、...

    微信小程序socket.io客户端, 支持微信小程序、支付宝小程序socket.io-mp-client-master.zip

    - 发送数据:`socket.emit('event-name', data);` - 监听数据:`socket.on('event-name', (res) =&gt; { console.log(res); });` 6. **错误处理**:库通常会提供错误处理机制,如连接失败、断线重连等,开发者需要...

    PyPI 官网下载 | python_socketio-3.0.1-py2.py3-none-any.whl

    4. **Namespace**:命名空间是SocketIO的一个特性,允许在一个SocketIO服务器上创建多个独立的通信通道。每个命名空间可以有不同的事件和逻辑,这样可以在同一个Socket连接上处理不同的数据类型或应用场景。 5. **...

    swift-socket.io-client-swiftSocket.IO客户端

    6. **API接口**:Swift Socket.IO客户端提供了丰富的API,如`emit()`用于发送事件,`on()`用于监听事件,以及`disconnect()`用于断开连接等,方便开发者集成到自己的应用中。 7. **错误处理**:客户端还提供了错误...

    PyPI 官网下载 | socketIO-client-2-0.7.4.tar.gz

    socketIO.emit('my_request', {'message': 'Hello Server!'}) ``` 在分布式和云原生环境(Cloud Native)中,socketIO-client因其实时性和可扩展性得到了广泛应用。例如,在Zookeeper这样的分布式协调服务中,...

    HiChat-master--计网设计.zip

    当用户输入消息并发送时,触发`emit`方法将消息发送到服务器。 - **展示聊天记录**:服务器广播的消息会在客户端接收到,更新聊天界面显示最新的聊天记录。 5. **HiChat-master项目分析** "HiChat-master--计网...

    使用MSIL采用Emit方式实现C#的代码生成与注入.rar

    MSIL使得开发者能够在运行时动态生成和执行代码,这是通过System.Reflection.Emit命名空间中的类来实现的。本教程将深入探讨如何使用MSIL和Emit API来生成和注入C#代码。 首先,让我们了解Emit API的基本概念。Emit...

    Interop Without PInvoke - Consuming Native Libraries in C#.-DynamicLib

    相反,C#的`System.Reflection.Emit`命名空间提供了动态生成IL代码的能力,这使得在运行时创建类型和方法成为可能。通过这种方式,我们可以动态地构建一个代理类,该类在内部调用本地库的函数,从而避免了P/Invoke的...

    AngularJS - Novice to Ninja.pdf.pdf )

    The Watchers in AngularJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 The $watchCollection() Function . . . . . . . . . . . . . . . . . . . . . . . 56 The $apply() Function ...

    Vue2.x和Vue3.x面试常问知识点-面试题-JackieDYH - CSDN博客.pdf

    - Vue2.x中,组件间通信可以通过props、事件($emit/$on)、Vuex状态管理或非父子组件间的Bus(事件总线)。 - Vue3.x增加了提供通信的新方法,如提供和注入(provide/inject)以及使用Context API。 面试中,...

    Emit学习之旅

    Emit学习之旅是一个深入探索C# Emit技术的教程,它为开发者提供了动态生成IL(Intermediate Language)代码的能力。Emit是.NET Framework的一部分,允许程序员在运行时构建和执行方法,这是实现元编程的重要工具。...

    高考英语作文万能模板附范文.doc

    - 解决方案一:One possible solution is ①-----------------------. Although it has the advantage of ②-----------------, there might be a potential drawback ③-----------------. - 解决方案二:Another ...

    learn-qt-qchart-plot-newdisplay.zip

    using namespace QtCharts; private: //曲线 QSplineSeries* line; //绘图变量和坐标 QChart* chart; //发来数据的接收槽函数 private slots: void receive_list(QVector&lt;QPointF&gt; list); 在子.cpp中 line =...

    socketIO使用教程--含代码demo.zip

    io.emit('message', data); }); }); ``` 3. **客户端集成**:在 HTML 页面中,引入 `socket.io-client` 库,并创建一个 `Socket` 实例,连接到服务器。然后,可以监听服务器发送的事件,以及发送自定义事件。 ...

    PyPI 官网下载 | fluent-logger-0.6.0.tar.gz

    然后,他们可以调用对象的方法来记录不同级别的日志事件,例如`emit()`方法,同时传递事件的标签和数据。 在分布式和云原生环境中,这种日志记录方案的优势在于它能够集中管理大量节点的日志,方便监控、搜索和分析...

    Node.js-node和socket.iol实现简易聊天室

    console.log(`Message "${msg}" stored in DB.`); } catch (error) { console.error(error); } io.emit('chat message', msg); }); ``` 这里我们创建了一个名为`messages`的表,用于存储聊天记录。当收到新...

Global site tag (gtag.js) - Google Analytics