2

.NET Emit 入门教程:第六部分:IL 指令:9:详解 ILGenerator 指令方法:运算操作指...

 1 month ago
source link: https://www.cnblogs.com/cyq1162/p/18133417
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

经过前面几篇的学习,我们了解到指令的大概分类,如:

参数加载指令,该加载指令以 Ld 开头,将参数加载到栈中,以便于后续执行操作命令。

参数存储指令,其指令以 St 开头,将栈中的数据,存储到指定的变量中,以方便后续使用。

创建实例指令,其指令以 New 开头,用于在运行时动态生成并初始化对象。

方法调用指令,该指令以 Call 开头,用于在运行时调用其它方法。

支条件指令,该指令通常以 Br、或 B、C 开头,用于在运行分支条件时跳转指令。

类型转换指令,该指令通常以 Cast、Conv 开头或box结尾,用于在运行时对类型进行转换。

本篇介绍运算操作指令,介绍完后,将结束指令篇。

第六部分:IL指令完整大纲目录如下:

.NET Emit 入门教程:第六部分:IL 指令:1:概要介绍

.NET Emit 入门教程:第六部分:IL 指令:2:详解 ILGenerator 辅助方法

.NET Emit 入门教程:第六部分:IL 指令:3:详解 ILGenerator 指令方法:参数加载指令

.NET Emit 入门教程:第六部分:IL 指令:4:详解 ILGenerator 指令方法:参数存储指令

.NET Emit 入门教程:第六部分:IL 指令:5:详解 ILGenerator 指令方法:创建实例指令

.NET Emit 入门教程:第六部分:IL 指令:6:详解 ILGenerator 指令方法:方法调用指令

.NET Emit 入门教程:第六部分:IL 指令:7:详解 ILGenerator 指令方法:分支条件指令

.NET Emit 入门教程:第六部分:IL 指令:8:详解 ILGenerator 指令方法:类型转换指令

.NET Emit 入门教程:第六部分:IL 指令:9:详解 ILGenerator 指令方法:运算操作指令(指令篇结束)

运算操作指令介绍:

在.NET Emit 编程中,运算操作指令是一类关键的IL(Intermediate Language)指令,用于在动态生成的代码中执行各种数学运算、位操作和比较操作。

这些指令允许开发人员对操作数进行加法、减法、乘法、除法、逻辑与、逻辑或、逻辑非、位与、位或、位异或、左移、右移以及比较等操作。

通过运算操作指令,开发人员能够在动态生成的代码中实现各种算术运算、逻辑运算和位操作,从而更灵活地处理数据和实现复杂的逻辑。

这些指令为动态代码生成提供了强大的功能,使得开发人员能够根据需要生成高效且功能丰富的代码。

运算操作指令的分类:

让我们按照分类逐一介绍各种指令以及它们的详细用途。

  1. 算术运算指令:

    • add(加法):将两个值相加,并将结果推送到计算栈上。主要用于执行整数和浮点数的加法操作。
    • sub(减法):将一个值减去另一个值,并将结果推送到计算栈上。用于执行整数和浮点数的减法操作。
    • mul(乘法):将两个值相乘,并将结果推送到计算栈上。用于执行整数和浮点数的乘法操作。
    • div(除法):将一个值除以另一个值,并将结果推送到计算栈上。用于执行整数和浮点数的除法操作。
  2. 逻辑运算指令:

    • and(与):对两个整数进行按位与操作,并将结果推送到计算栈上。用于执行逻辑与操作。
    • or(或):对两个整数进行按位或操作,并将结果推送到计算栈上。用于执行逻辑或操作。
    • xor(异或):对两个整数进行按位异或操作,并将结果推送到计算栈上。用于执行逻辑异或操作。
  3. 位操作指令:

    • shl(左移):将一个整数向左移动指定的位数,并将结果推送到计算栈上。用于执行左移操作。
    • shr(右移):将一个整数向右移动指定的位数,并将结果推送到计算栈上。用于执行算术右移操作。
    • not(非):对一个整数进行按位取反操作,并将结果推送到计算栈上。用于执行按位取反操作。
  4. 比较操作指令:

    • ceq(相等比较):比较两个值是否相等,并将结果推送到计算栈上。用于执行相等比较操作。
    • clt(小于比较):比较一个值是否小于另一个值,并将结果推送到计算栈上。用于执行小于比较操作。
    • cgt(大于比较):比较一个值是否大于另一个值,并将结果推送到计算栈上。用于执行大于比较操作。

这些指令提供了丰富的功能,可以用于执行各种数学运算、逻辑运算、位操作和比较操作,从而实现各种复杂的编程逻辑。在动态生成的代码中,开发人员可以根据具体需求使用这些指令来实现所需的功能。

接下来,我们对一个指令分类,分别给出一个示例,来介绍它们的基本用法。

对于运行指令,有两个指令后缀:

_ovf: 进行溢出检查。

_un:无符号。

17408-20240413215134074-1582514196.png

多数指令都带有这两个后缀,理解这两个后缀的意思,可以快速理解所有该后缀指令。 

1、算术运算指令:

Add 指令:

17408-20240413214920216-1728458729.png

示例代码:

 MethodBuilder methodBuilder = tb.DefineMethod("MathTo", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int),typeof(int) });

 ILGenerator il = methodBuilder.GetILGenerator();

 il.Emit(OpCodes.Ldarg_0);
 il.Emit(OpCodes.Ldarg_1);
 il.Emit(OpCodes.Add_Ovf_Un);

 il.Emit(OpCodes.Ret);     // 返回该值

对应代码:

17408-20240413215342954-580765299.png

其它指令使用方式一样,省去重复举例。

Add 指令对应C#代码:+
Sub 指令对应C#代码:-
Mul 指令对应C#代码:*
Div 指令对应C#代码:/

2、逻辑运算指令:

17408-20240413215728057-390200510.png

 示例代码:

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.And);

il.Emit(OpCodes.Ret);     // 返回该值

对应代码:

17408-20240413215812644-728309969.png

使用方式和算术指令运行其实一致:

 And 指令对应C#代码:&
  Or  指令对应C#代码:|
 Xor  指令对应C#代码:^

3、位操作指令:

Shl 指令:左移指令,Shift Left 的简写

17408-20240413220340753-986718149.png

示例代码:

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4,2);
il.Emit(OpCodes.Shl);

il.Emit(OpCodes.Ret);     // 返回该值

对应代码: 

17408-20240413220604770-1664754699.png

使用方式,需要在第二个参数,指定要位移的位数。

右移操作的方式和左移一样。

Not 指令:按位取反

17408-20240413221201073-924935678.png

示例代码:

MethodBuilder methodBuilder = tb.DefineMethod("MathTo", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[] { typeof(int) });

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Not);

il.Emit(OpCodes.Ret);     // 返回该值

对应代码:

17408-20240413221215545-935344326.png

右移操作的方式和左移一样,而 Not 指令则不需要第二个参数:

Shl 指令对应C#代码:<<
Shr 指令对应C#代码:>>
Not 指令对应C#代码:~

4、比较操作指令:

Ceq 指令:比较两个值

17408-20240413222109028-306436869.png

 示例代码:

var dynamicMethod = new DynamicMethod("ConvertTo", typeof(bool), new Type[] { typeof(int), typeof(float) }, typeof(AssMethodIL_Condition));

ILGenerator il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ceq);

il.Emit(OpCodes.Ret);     // 返回该值

var result = dynamicMethod.Invoke(null, new object[] { 11, 11 });
Console.WriteLine(result);
Console.Read();

对应代码:

17408-20240413222046451-1713127874.png

运行结果:

17408-20240413221924735-719017114.png

其它两个指令使用方式和 Ceq 一致:

Ceq 指令对应C#代码:==
Clt 指令对应C#代码:<
Cgt 指令对应C#代码:>

如何实现 >= 或 <=

由于没有对应的指令,所以需要用点小技巧组合,来实现该代码:

用Clt + Ceq 指令实现:>=

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Clt);

il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ceq);

il.Emit(OpCodes.Ret);     // 返回该值

对应代码:

17408-20240413223732924-165366906.png

用Cgt + Ceq 指令实现:<=

ILGenerator il = methodBuilder.GetILGenerator();

il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Cgt);

il.Emit(OpCodes.Ldc_I4, 0);
il.Emit(OpCodes.Ceq);

il.Emit(OpCodes.Ret);     // 返回该值

对应代码:

17408-20240413223915878-1262491344.png

性能小细节提醒:

平时我们写代码,涉及 >= 或 <= 的整数字判断时候:

比如:a>=2(需要两条指令),可以写成 a>3(只要一条指令)。

在.NET Emit编程中,我们探讨了运算操作指令的重要性和应用。

这些指令包括各种数学运算、位操作和比较操作,能够在动态生成的代码中实现对数据的处理和操作。

通过这些指令,开发人员可以灵活地进行算术运算、逻辑运算和比较操作,从而实现各种复杂的算法和逻辑。

在实际应用中,我们可以利用这些指令来实现诸如加密算法、数值计算、逻辑判断、数据压缩等功能。

通过深入理解和熟练运用这些运算操作指令,开发人员可以提高动态代码生成的效率和灵活性,从而更好地满足各种编程需求。

同时,对ILGenerator指令方法的进一步学习也能够帮助开发人员更加灵活地控制动态生成的代码,实现更复杂的功能和逻辑。

本篇之后,将进入第七部分:实战项目


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK