2016-10-26 15 views
11

ライブラリのメソッド呼び出し時間に関する統計を作成しようとしています。 各メソッド呼び出しを行のあるライブラリにラップして追跡するのではなく、これらの繰り返し手順を実行する汎用アクションと関数を作成したいと思います。C#の汎用関数宣言

など。値を返さないメソッドのために、私が作成したこの:

timedAction("methodname",() => lib.methodname())で呼び出すことができ
private readonly Action<string, Action> timedAction = (name, action) => 
    { 
     var sw = Stopwatch.StartNew(); 
     action.Invoke(); 
     trackDuration(name, sw.ElapsedMilliseconds); 
    }; 

値を返すメソッドでも同様のことをしたいが、明らかにActionは値を返すことができないため、その目的には使用できません。

一般的なFuncでこれを行う方法はありますか?したがって、ライブラリメソッドパラメータの組み合わせごとに宣言する必要はありませんか?

+5

このタイプのものについては、PostSharpのようなAOPソリューションを見たいと思うかもしれません。 –

+0

また、[Tuple.Create](https://msdn.microsoft.com/en-us/library/system.tuple.create(v = vs.110).aspx)メソッドグループを検討してください。任意の数のジェネリックを受け入れるメソッドを書く方法はありません。 –

+0

呼び出し側は、 ''アクション ''として、 '' timedAction( "methodname"、()=> {lib.methodname(1,2,3);}) 'という文を作るだけで、任意の呼び出しを自明に渡すことができます。 –

答えて

5

あなたはこのような一般的な機能を使用することができます。

private static TValue FuncHandler<TValue>(string name, Func<TValue> func) 
{ 
    var sw = Stopwatch.StartNew(); 
    var result = func(); 

    trackDuration(name, sw.ElapsedMilliseconds); 

    return result; 
} 

はこのようにそれを呼び出します。

https://dotnetfiddle.net/5PLCmM

var result = FuncHandler("name",() => MyMethod(param1)); 
+0

'func()'の返り値は常に 'TValue'型ですが、そこにキャストする必要はないと思います。 – thehennyy

+0

あなたは正しいです。私は私の答えを編集しました。 – selami

+0

ありがとう、これはまさに私が探していたものでした。私は 'FuncHandler'のプレースホルダーをメソッドの入力パラメーターに指定しなければならないと考えましたが、これはうまくいきます。 – RasmusW

1

実際、AOPはあなたに退屈のこの種以上を購入します

// 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について知っています。

'これは役立ちます。

+2

私はOPの既存の 'Stopwatch'コードを' DateTime'に置き換える理由を知ることはできません。そして、その追加のステップの怠惰を呼び出してください。 – Groo

+0

@Groo。私も私の電話機にいて、dotnetfiddleはシステム診断の名前空間で私に衝突し続けました。ありがとう。 – YSharp

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

0

が、私はこの質問にはすでに答えを持って知っているが、私はこのソリューションはことができると思います面白いですが、毎回あなたが名前を渡す必要がない場合は、これを行うことができます:
(それは@selamiの答えに非常に触発されました。私は、このソリューションの上にベンチマークを実行しなかったが、私はあなたが少しパフォーマンスヒットに遭遇する必要がありますね、しかし、あなたは気にしない場合

public void TimeMethod(Expression<Action> actionExpression) 
{ 
    TimedMethodInvoke(actionExpression); 
} 

public TValue TimeMethod<TValue>(Expression<Func<TValue>> funcExpression) 
{ 
    return (TValue)TimedMethodInvoke(funcExpression); 
} 

:)

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; 
} 

そして、あなたの最終的な方法それについては、毎回名前を入力するのを避けたい場合は、これが役に立ちます。