在优化C#代码或对比某些API的效率时,通常需要测试某个方法的运行时间,可以通过DateTime来统计指定方法的执行时间,也可以使用命名空间System.Diagnostics中封装了高精度计时器QueryPerformanceCounter方法的Stopwatch类来统计指定方法的执行时间:

  1.使用DateTime方法:

DateTime dateTime = DateTime.Now;
MyFunc();
Console.WriteLine((DateTime.Now - dateTime).TotalMilliseconds);

  2.使用Stopwatch方式:

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
MyFunc();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds); //本次MyFunc()方法的运行毫秒数
//重置计时器
stopwatch.Restart(); //此处可以使用stopwatch.Reset(); stopwatch.Start();组合代替
MyFunc();
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds); //本次MyFunc()方法的运行毫秒数

  以上两种办法都可以达到获取方法执行时间的目的,但是在需要对整个项目中的方法都进行监测用时时,除了使用性能分析工具,我们还可以通过代码注入的方式给程序集中每一个方法加入计时器;

  通过命名空间System.Reflection.Emit中的类可以动态的创建程序集、类型和成员,通常类库Mono.Cecil可以动态读取并修改已经生成的IL文件,这种在不修改源代码的情况下给程序集动态添加功能的技术称为面向切面编程(AOP);

  这里给出了一个注入使用Stopwatch来检测方法执行时间的代码,这里的Mono.Cecil类库可以通过nuget进行安装:

using System;
using System.IO;
using System.Linq;
using System.Diagnostics;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Collections.Generic;
    static void Main(string[] args)
{
for (int i = ; i < args.Length; i++)
{
FileStream fileStream = new FileStream(args[i], FileMode.Open);
if (fileStream != null)
{
AssemblyDefinition aD = AssemblyDefinition.ReadAssembly(fileStream);
ModuleDefinition mD = aD.MainModule;
Collection<TypeDefinition> typeDefinition = mD.Types;
foreach (TypeDefinition type in typeDefinition)
{
if (type.IsClass)
{
foreach (MethodDefinition method in type.Methods)
{
if (method.IsPublic && !method.IsConstructor)
{
ILProcessor il = method.Body.GetILProcessor();
TypeReference stT = mD.ImportReference(typeof(Stopwatch));
VariableDefinition stV = new VariableDefinition(stT);
method.Body.Variables.Add(stV);
Instruction first = method.Body.Instructions.First();
il.InsertBefore(first, il.Create(OpCodes.Newobj,
                      mD.ImportReference(typeof(Stopwatch).GetConstructor(new Type[] { }))));
il.InsertBefore(first, il.Create(OpCodes.Stloc_S, stV));
il.InsertBefore(first, il.Create(OpCodes.Ldloc_S, stV));
il.InsertBefore(first, il.Create(OpCodes.Callvirt,
                      mD.ImportReference(typeof(Stopwatch).GetMethod("Start")))); Instruction @return = method.Body.Instructions.Last();
il.InsertBefore(@return, il.Create(OpCodes.Ldloc_S, stV));
il.InsertBefore(@return, il.Create(OpCodes.Callvirt,
                      mD.ImportReference(typeof(Stopwatch).GetMethod("Stop")))); il.InsertBefore(@return, il.Create(OpCodes.Ldstr, $"{method.FullName} run time: "));
il.InsertBefore(@return, il.Create(OpCodes.Ldloc_S, stV));
il.InsertBefore(@return, il.Create(OpCodes.Callvirt,
                      mD.ImportReference(typeof(Stopwatch).GetMethod("get_ElapsedMilliseconds"))));
il.InsertBefore(@return, il.Create(OpCodes.Box, mD.ImportReference(typeof(long))));
il.InsertBefore(@return, il.Create(OpCodes.Call,
                      mD.ImportReference(typeof(string).GetMethod("Concat", new Type[] { typeof(object), typeof(object) }))));
il.InsertBefore(@return, il.Create(OpCodes.Call,
                      mD.ImportReference(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }))));
}
}
}
}
FileInfo fileInfo = new FileInfo(args[i]);
string fileName = fileInfo.Name;
int pointIndex = fileName.LastIndexOf('.');
string frontName = fileName.Substring(, pointIndex);
string backName = fileName.Substring(pointIndex, fileName.Length - pointIndex);
string writeFilePath = Path.Combine(fileInfo.Directory.FullName, frontName + "_inject" + backName);
aD.Write(writeFilePath);
Console.WriteLine($"Success! Output path: {writeFilePath}");
fileStream.Dispose();
}
}
Console.Read();
}

  完整的项目传到了Github上=>InjectionStopwatchCode,下载项目后,通过dotnet build命令即可编译出可执行程序,将目标程序集文件拖入到该应用程序即可在程序集目录导出注入代码后的程序集文件,经过测试,包括方法拥有返回值和方法的参数列表中包含out和ref参数等情况都不会对运行结果产生影响;

  示例:

using System;

public class MyClass
{
public void MyFunc()
{
int num = ;
for (int i = ; i < int.MaxValue; i++)
{
num++;
}
}
}
public class Program
{
public static void Main(string[] args)
{
MyClass myObj = new MyClass();
myObj.MyFunc();
Console.Read();
}
}

  原始IL代码:

  代码注入后IL代码:

  代码注入后运行结果:


如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!

作者:Minotauros
出处:https://www.cnblogs.com/minotauros/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

获取C#中方法的执行时间及其代码注入的更多相关文章

  1. 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码

    详解C#泛型(二)   一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...

  2. Java学习笔记(七)——获取类中方法的信息,java的LinkedList

    [前面的话] 在实际项目中学习知识总是最快和最有效的,既能够较好的掌握知识,又能够做出点东西,还是简单的知识总结,最近一直在总结笔记,写的东西还是比较水,希望慢慢可以写出一些干货. 学习过程中的小知识 ...

  3. Instruments10 分析某个类中方法的执行时间

    此步骤也可用户内存分配.内存泄漏的检测 最新操作步骤参考: https://www.jianshu.com/p/e499ce63ed72

  4. CodeIgniter(3.1.4)框架中添加执行时间统计代码

    CodeIgniter(3.1.4)框架中添加,执行时间统计代码: system/core/CodeIgniter.php最后行处. /* * ---------------------------- ...

  5. js 获取iframe中的元素

    今天要修改编辑器插件中的元素遇到的问题 jquery 在父窗口中获取iframe中的元素 1.Js代码 格式:$("#iframe的ID").contents().find(&qu ...

  6. 深入理解xLua基于IL代码注入的热更新原理

    目前大部分手游都会采用热更新来解决应用商店审核周期长,无法满足快节奏迭代的问题.另外热更新能够有效降低版本升级所需的资源大小,节省玩家的时间和流量,这也使其成为移动游戏的主流更新方式之一. 热更新可以 ...

  7. js获取输入框中当前光标位置并在此位置插入字符串的方法(angularjs+ts)

    一半是参照别人代码,一半是自己代码,略笨拙,如果有更好的方法希望分享. 获取当前光标位置的方法 getCaretPosition (obj:any) { //获取输入框中当前光标的位置,obj为此输入 ...

  8. JS获取URL中参数值(QueryString)的4种方法分享<转>

    方法一:正则法 复制代码代码如下: function getQueryString(name) {    var reg = new RegExp('(^|&)' + name + '=([^ ...

  9. 009. C#中的WebBrowser控件的属性、方法及操作演示代码(转)

    本文转自 http://www.open-open.com/code/view/1430559996802 0.常用方法 Navigate(string urlString):浏览urlString表 ...

随机推荐

  1. Centos7下安装Docker[z]

    [z]https://www.cnblogs.com/qgc1995/p/9553572.html https://yq.aliyun.com/articles/691610?spm=a2c4e.11 ...

  2. linux下sort命令使用详解---linux将文本文件内容加以排序命令

    转载自:http://www.cnblogs.com/hitwtx/archive/2011/12/03/2274592.html linux下sort命令使用详解---linux将文本文件内容加以排 ...

  3. Django模型层(2)

    <!DOCTYPE html><html lang="zh-cn"><head><meta charset="utf-8&quo ...

  4. Java 日志体系

    Java 日志体系 <java 日志和 SLF4J 随想>:http://ifeve.com/java-slf4j-think/ 一.常用的日志组件 名称 jar 描述 log4j log ...

  5. MD5加密算法的Java版本

    网上搜索Java实现MD5的资料很多,错误的也很多. 之前编写的一个阿里云直播鉴权原理算法需要用到MD5算法,网上找了几个,都是不行,浪费了时间,现在贴一个,做备用. import java.secu ...

  6. lwip-动态内存管理

    动态内存管理涉及两类重要函数,内存分配函数,内存释放函数,如C语言中的malloc和free. 内存分配的本质是:在事先准好一大块内存堆(可以理解为一个很大的数组)中分配合适的空间,然后将该空间起始地 ...

  7. C#延时函数

    用Thread方法: 先using system.threading; 再在需要延时的进程处插入 thread.sleep(int);

  8. python函数(一)

    python函数(一) 1.函数的定义: def test(): print('test is running...') return 定义一个函数,有3个部分需要注意: 函数名称.函数的命名规范与变 ...

  9. JavaScript基础视频教程总结(041-050章)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  10. noip第20课资料