Объявление общей функции в С#
Я пытаюсь создать некоторую статистику о длительности вызова метода в библиотеке.
Вместо того чтобы обернуть вызов каждого метода в библиотеку с помощью строк по времени и отслеживать его, я хочу создать общее действие и функцию, которые выполняют эти повторяющиеся шаги.
например. для методов, которые не возвращают значение, я создал это:
private readonly Action<string, Action> timedAction = (name, action) =>
{
var sw = Stopwatch.StartNew();
action.Invoke();
trackDuration(name, sw.ElapsedMilliseconds);
};
Это можно вызвать с помощью timedAction("methodname", () => lib.methodname())
.
Я хочу сделать что-то подобное для методов, возвращающих значение, но, очевидно, Action
не может использоваться для этой цели, поскольку он не может вернуть значение.
Есть ли способ сделать это с помощью общего Func
, поэтому мне не нужно объявлять один для каждой комбинации параметров библиотечного метода?
Ответы
Ответ 1
Вы можете использовать такую общую функцию, как это:
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));
Ответ 2
В самом деле, АОП купит вам больше, чем эта скука:
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, было просто слишком лениво, чтобы поместить его там)
'Надеюсь, что это поможет.
Ответ 3
В вашем случае АОП будет более утомительным. Вот мое решение, которое работает:
![введите описание изображения здесь]()
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.
![введите описание изображения здесь]()
Ответ 4
Я знаю, что у этого вопроса уже есть ответ, но я думаю, что это решение может быть интересным, если вы не хотите, чтобы вам нужно было передавать имя, каждый раз, вы могли бы это сделать:
(Это было очень вдохновлено ответом @selami.)
private MemberInfo GetMethodName<T>(Expression<T> expression)
{
Expression body = expression.Body;
// You might want to complete this
// depending on which expression you want to use
return ((MethodCallExpression)body).Method.Name;
}
// Works for both Action and Func
private object TimedMethodInvoke<T>(Expression<T> funcExpression)
{
var sw = Stopwatch.StartNew();
var result = ((Delegate)(object)funcExpression.Compile()).DynamicInvoke();
trackDuration(GetMethodName(funcExpression), sw.ElapsedMilliseconds);
return result;
}
И ваши окончательные методы:
public void TimeMethod(Expression<Action> actionExpression)
{
TimedMethodInvoke(actionExpression);
}
public TValue TimeMethod<TValue>(Expression<Func<TValue>> funcExpression)
{
return (TValue)TimedMethodInvoke(funcExpression);
}
Я не тестировал этот вариант, но думаю, вы должны столкнуться с небольшим успехом, но если вы не возражаете об этом и хотите избежать ввода имени каждый раз, это может помочь.