C#中的泛型函数声明

时间:2016-10-26 07:04:51

标签: c# generics

我正在尝试创建一些关于库中方法调用持续时间的统计信息。 我没有用时间线将每个方法调用包装到库中并跟踪它,而是想创建一个通用的动作和函数来执行这些重复的步骤。

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执行此操作,因此我不必为每个库方法参数组合声明一个?

4 个答案:

答案 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将更加乏味。这是我的解决方案:

enter image description here

的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.

enter image description here

答案 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;

我没有针对此解决方案运行基准测试,但我猜您应该会遇到一点性能损失,但如果您不介意这一点并希望避免输入每个名称时间,这可能有所帮助。