2016-12-27 101 views
0

我需要编写一个方法,将消耗其他方法作为“动作”来处理它(例如日志记录)。功能<T>与动态数量的参数类型

这里是我的方法:

public static void Call(Func<T, TResult> action){ 
    Log.Debug(action.Method.Name + " method called"); 
} 

,现在我想在每一个可能的方法坚持,像

private void Method1 (string param1, int param2) { ...} 
private ServiceResult Method2 (UserInfo param1, string param2, bool param3, double param4) { ... } 

,我想这样称呼它

Call(Method1); // output: "Method1 method called" 
Call(Method2); // output: "Method2 method called" 

我问题是,我需要为我的Call-Me声明“Func <>”参数的修复参数类型的ThOD。我怎么能以动态/通用的方式做到这一点?编辑:我在.NET Compact Framework上工作,所以我只有.NET 3.5 CF可用,不能使用每个类或类型。

+1

如果你不知道在编译时的方法名,看看'[CallerMemberName]'。如果你这样做,使用'nameof'。 – zaitsman

+2

除了其他任何东西,从代码可读性的角度来看,这听起来并不好听......你真的不想为每个方法手动添加一些东西(并且必须阅读代码)。您可能想要查看.NET的AOP框架。 –

回答

2

由于所有从您Func<T,TResult>需要的是Method,从中收获了它的名字,就可以使用Delegate类,使所有与会代表,包括Func<...>,继承:

public static void Call(Delegate action){ 
    Log.Debug(action.Method.Name + " method called"); 
} 
3

如果你只是想访问代表的元数据,使用Delegate类:

public static void Call(Delegate action){ 
    Log.Debug(action.Method.Name + " method called"); 
} 

如果你想呼叫代表,THI ngs变得更复杂:

有9个版本的Acton和9个版本的Func:每个从零到八个参数。如果您希望直接支持这些代表的所有变体,则需要18次重载您的Call方法。

这似乎过分了,它实际上是,尤其是因为您还需要在代表中的每个参数的方法中添加一个参数。我会建议去与两个重载和使用lambda表达式:

public static void Call<T>(Func<T> action) 
{ 
    return action(); 
} 

public static void Call(Action action) 
{ 
    action(); 
} 

private void ExampleWithParams(int param1) 
{ 
    //... 
} 

private int ExampleWithParamsAndReturn(int param1) 
{ 
    //... 
    return param1; 
} 

打电话给他们这样的:

Call(() => ExampleWithParams(0)); 

int value = Call(() => ExampleWithParamsAndReturn(0)); 
1

如果你不介意使用lambda表达式,那么这些将允许你检查这两个被调用的方法及其参数。正如我将展示的,参数甚至可以是方法调用本身。

这里是一个工作的例子,你可以在一个控制台应用程序坚守测试:

private static void TestLogAnyMethod() 
{ 
    Console.WriteLine("\r\n>> First batch >>"); 
    var x = M1(0, 1.1); 
    var y = M2("a", true); 
    var z = M2(GetA(4, 5.0/7.0), GetTrue()); 

    Console.WriteLine("\r\n>> Second batch >>"); 
    var x2 = Log(() => M1(0, 1.1)); 
    var y2 = Log(() => M2("a", true)); 
    var z2 = Log(() => M2(GetA(4, 5.0/7.0), GetTrue())); 

    Console.WriteLine("\r\n>> Results >>"); 
    Console.WriteLine(x + " == " + x2); 
    Console.WriteLine(y + " == " + y2); 
    Console.WriteLine(z + " == " + z2); 
} 

private static string GetA(int p, double q) 
{ 
    Console.WriteLine("-- Executing GetA"); 
    return "a"; 
} 

private static bool GetTrue() 
{ 
    Console.WriteLine("-- Executing GetTrue"); 
    return true; 
} 

private static string M1(int v1, double v2) 
{ 
    return "123abc"; 
} 

private static bool M2(string w1, bool w2) 
{ 
    return true; 
} 

private static T Log<T>(Expression<Func<T>> expr) 
{ 
    var funcBody = (MethodCallExpression)expr.Body; 
    var funcArgs = funcBody.Arguments; 
    Console.WriteLine("Method call: " + funcBody.Method.Name + "(" + string.Join(", ", funcArgs.Select(p => p.ToString())) + ")"); 

    Func<T> f = expr.Compile(); 
    return f(); 
} 

我用不平凡的测试用例来说服自己,木屐()和GetTrue()被调用一次内部日志(...),更具体地说,只有当执行f();时。

输出:

>> First batch >> 
-- Executing GetA 
-- Executing GetTrue 

>> Second batch >> 
Method call: M1(0, 1.1) 
Method call: M2("a", True) 
Method call: M2(GetA(4, 0.714285714285714), GetTrue()) 
-- Executing GetA 
-- Executing GetTrue 

>> Results >> 
123abc == 123abc 
True == True 
True == True 
+0

它就像一个魅力!不幸的是我在.NET Compact Framework上工作,并且“表达式”在那里不可用。我应该在我的问题中添加这些信息。但也是完美的答案 – Surras