在优化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搭建nexus私服

    1.保证JDK,MAVEN已安装,firewalld服务安装 PS:yum install firewalld 2.官网下载:https://www.sonatype.com/download-oss ...

  2. Map 数据结构

    JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键.这给它的使用带来了很大的限制. 为了解决这个问题,ES6 提供了 Map 数据结构. ...

  3. javaweb开发.页面中文乱码问题

    1.设置eclips , window->Preferences->web->JSP Files中的Encoding选项为UTF-8 2.修改jsp文件头部为UTF-8 <%@ ...

  4. 【Spark2.0源码学习】-7.Driver与DriverRunner

         承接上一节内容,Client向Master发起RequestSubmitDriver请求,Master将DriverInfo添加待调度列表中(waitingDrivers),下面针对于Dri ...

  5. python04 列表 元祖 字典

    1.list 有序,元素可以被修改 li=[1,2,2,"am","123"] 列表中的元素可以是数字,字符串,列表等等 支持切片,切片结果为列表  li[3] ...

  6. 网络传输 buf 封装 示例代码

    网络传输 buf 封装 示例代码 使用boost库 asio // BufferWrapTest.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h&quo ...

  7. john and hydra using de-ice1.100

    配置IP  ipconfig etho 192.168.179.111 http://192.168.179.111/index2.php curl http://192.168.179.111/in ...

  8. Python11/23--mysql用户管理/pymysql

    1.mysql用户管理 定义:数据安全是很重要的,不能随便分配root账户,应该按照不同开发岗位分配不同的账户和权限 mysql中将用户相关的数据放在mysql库中 user→db→tables_pr ...

  9. ABP框架系列之二十一:(Domain-Services-领域服务)

    Introduction Domain Services (or just Service, in DDD) is used to perform domain operations and busi ...

  10. MySQL八、备份和还原

                MySQL 八.数据库备份和还原       1.二进制日志相关配置     1)查看使用中的二进制日志文件列表,及大小   SHOW {BINARY | MASTER} LO ...