在优化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. Android后台监控指定app的输入内容,抢红包,模拟点击原理

    Android开启辅助功能之后可以用AccessibilityService 去后台监控指定的app的输入内容,也可以监控到app的动作 以及通知栏的动作, 抢红包其实就根据通知栏出现了红包的通知消息 ...

  2. Mybatis配置问题解决Invalid bound statement (not found)

    首先这个异常的原因是系统根据Mapper类的方法名找不到对应的映射文件. 网上也搜索了到了类似的文章,一般可以从以下几个点排查: mapper.xml的namespace要写所映射接口的全称类名,而且 ...

  3. Android开发日常-listview滚动方法梳理

    listview滚动方法梳理 1.setSelection(position); 滚动到指定条目 2.setSelectionFromTop(position,y): 距离指定条目向下偏移y(像素) ...

  4. PHP 实现单链表

    数据结构是计算机存储.组织数据的方式,结构不同那么数据的检索方式和效率都不一样, 常用的数据结构有  数组 .栈 .队列 .链表 .树.堆 今天讲下单链表,单链表是一种链式存取的数据结构, 跟顺序链表 ...

  5. [翻译]高并发框架 LMAX Disruptor 介绍

    原文地址:Concurrency with LMAX Disruptor – An Introduction 译者序 前些天在并发编程网,看到了关于 Disruptor 的介绍.感觉此框架惊为天人,值 ...

  6. org.apache.hadoop.security.AccessControlException

    在hdfs集群上,需要向Hdfs写入文件,控制台会输出以下错误信息: Caused by: org.apache.hadoop.ipc.RemoteException(org.apache.hadoo ...

  7. [TestNG] Eclipse/STS中两种安装TestNG的方法

    Two Ways To Install TestNG in Eclipse/STS Today I install the newest Sprint Tool Suite and want to i ...

  8. django的简单原理

    一.自定义客户端和服务端的请求响应 1.客户端打开url,向服务器发出请求 2.服务端用socket写一个py,用于接收请求和做出响应 3.服务端接收请求 4.服务端模拟HTTP协议做出响应,状态行为 ...

  9. modal 移除遮盖层

    弹框关闭时 移除遮盖层 $("#modal").bind('hide.bs.modal',function(){ $(".modal-backdrop").re ...

  10. 使用itext生成pdf的,各种布局

    代码如下,jar包为itext.jar,itextAsia.jar,最好都是最新的 :2张图片也在最后贴出,把图片放到D盘可以直接生成制定格式的pdf. 最后生成的pdf如下: 代码如下: packa ...