我正在尝试创建一些关于库中方法调用持续时间的统计信息。 我没有用时间线将每个方法调用包装到库中并跟踪它,而是想创建一个通用的动作和函数来执行这些重复的步骤。
E.g。对于没有返回值的方法,我创建了这个:
private readonly Action<string, Action> timedAction = (name, action) =>
{
var sw = Stopwatch.StartNew();
action.Invoke();
trackDuration(name, sw.ElapsedMilliseconds);
};
可以使用timedAction("methodname", () => lib.methodname())
调用。
我想为返回值的方法做类似的事情,但显然Action
不能用于此目的,因为它不能返回值。
有没有办法用通用Func
执行此操作,因此我不必为每个库方法参数组合声明一个?
答案 0 :(得分:5)
您可以使用这样的通用函数:
private static TValue FuncHandler<TValue>(string name, Func<TValue> func)
{
var sw = Stopwatch.StartNew();
var result = func();
trackDuration(name, sw.ElapsedMilliseconds);
return result;
}
这样称呼:
var result = FuncHandler("name", () => MyMethod(param1));
答案 1 :(得分:1)
事实上,AOP会给你买的不仅仅是这种单调乏味:
https://dotnetfiddle.net/5PLCmM
// Needs to be replicated after Func<T1, TResult>, Func<T1, T2, TResult>, etc, for all the functions arities you'll want to wrap with it
public static TResult Timed<T1, /*T2, etc*/TResult>(out long duration, Func<T1, /*T2, etc*/TResult> func, T1 arg1/*T2 arg2, etc*/)
{
//start timing
var t0 = DateTime.Now;
var result = func(arg1/*, arg2, etc*/);
//end timing
duration = (long)DateTime.Now.Subtract(t0).TotalMilliseconds;
return result;
}
public int Factorial(int n)
{
return n > 0 ? n * Factorial(n - 1) : 1;
}
public int Fibonacci(int n)
{
return n > 1 ? Fibonacci(n - 2) + Fibonacci(n - 1) : n;
}
public static void Main()
{
var program = new Program();
long duration;
var _12bang = Timed(out duration, program.Factorial, 12);
Console.WriteLine("{0}! = {1} in {2} ms", 12, _12bang, duration);
var fib31 = Timed(out duration, program.Fibonacci, 31);
Console.WriteLine("Fib {0} = {1} in {2} ms", 31, fib31, duration);
}
(是的,我知道StopWatch;只是懒得把它放在那里)
&#39;希望这会有所帮助。
答案 2 :(得分:1)
在你的情况下,AOP将更加乏味。这是我的解决方案:
的Class1.cs
using System;
namespace ClassLibrary1
{
public class Class1
{
public void WriteNoParam()
{
Console.WriteLine("void");
}
public void WriteWithParam(string name)
{
Console.WriteLine("My name is: " + name);
}
}
}
Program.cs的
using System;
namespace ConsoleApplication2
{
using System.Diagnostics;
using System.Reflection;
using ClassLibrary1;
class Program
{
static void Main(string[] args)
{
var prReflection = new TestReflection<Class1>();
var elapsed = prReflection.TestFunc(new Class1(), @"C:\Users\yasir\Documents\visual studio 2013\Projects\ConsoleApplication2\ClassLibrary1\bin\Debug\ClassLibrary1.dll", "WriteNoParam", new string[0]);
Console.WriteLine("Elapsed time for non parameter method: "+elapsed);
elapsed = prReflection.TestFunc(new Class1(), @"C:\Users\yasir\Documents\visual studio 2013\Projects\ConsoleApplication2\ClassLibrary1\bin\Debug\ClassLibrary1.dll", "WriteWithParam", new[]{"Yasir"});
Console.WriteLine("Elapsed time for parameter method: " + elapsed);
Console.ReadLine();
}
}
public class TestReflection<T> where T: class
{
public Func<T, string, string, string[], long> TestFunc = (arg1, s, s2, arr) =>
{
var assembly = Assembly.LoadFile(s);
var type = assembly.GetType(typeof (T).ToString());
long executionTime;
if (type != null)
{
var methodInfo = type.GetMethod(s2);
if (methodInfo != null)
{
ParameterInfo[] parameters = methodInfo.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
var stopWatch = new Stopwatch();
if (parameters.Length == 0)
{
// This works fine
stopWatch.Start();
methodInfo.Invoke(classInstance, null);
return stopWatch.ElapsedMilliseconds;
}
stopWatch.Start();
methodInfo.Invoke(classInstance, arr); ;
return stopWatch.ElapsedMilliseconds;
}
}
return 0;
};
}
}
我已经在调试模式下运行,以测试控制台是否能够在几毫秒内输出并且它可以正常工作。
如果你不在调试中运行,执行将非常快,控制台将输出0.
答案 3 :(得分:0)
我知道这个问题已经有了答案,但我认为这个解决方案很有意思,如果你不想自己传递这个名字,你每次都可以这样做:
(很多灵感来自@ selami&#39;回答。)
public void TimeMethod(Expression<Action> actionExpression)
{
TimedMethodInvoke(actionExpression);
}
public TValue TimeMethod<TValue>(Expression<Func<TValue>> funcExpression)
{
return (TValue)TimedMethodInvoke(funcExpression);
}
你最后的方法:
SELECT ID, month
FROM YourTable T
JOIN (
SELECT 'JAN' as month,'2016-01-01' as start,'2016-01-31' as end UNION ALL
SELECT 'FEB' as month,'2016-02-01' as start,'2016-02-29' as end UNION ALL
SELECT 'MAR' as month,'2016-03-01' as start,'2016-03-31' as end UNION ALL
SELECT 'APR' as month,'2016-04-01' as start,'2016-04-30' as end UNION ALL
SELECT 'MAY' as month,'2016-05-01' as start,'2016-05-31' as end UNION ALL
SELECT 'JUN' as month,'2016-06-01' as start,'2016-06-30' as end UNION ALL
SELECT 'JUL' as month,'2016-07-01' as start,'2016-07-31' as end
) M
ON T.AP_From <= M.end
AND T.AP_To >= M.start;
我没有针对此解决方案运行基准测试,但我猜您应该会遇到一点性能损失,但如果您不介意这一点并希望避免输入每个名称时间,这可能有所帮助。