【发布时间】:2014-05-14 21:12:35
【问题描述】:
我有一个相当复杂的问题。我正在尝试从方法及其形式参数和实际参数中获取唯一键。该方法的目标是进行方法调用,并根据 1) 类和方法的名称以及 2) 调用它的参数的名称和值返回一个唯一键。
该方法看起来像这样(抱歉所有细节,但我找不到合理的方法来缩小示例但仍能解释我的问题)
public class MethodKey
{
public static string GetKey<T>(Expression<Func<T>> method, params string[] paramMembers)
{
var keys = new Dictionary<string, string>();
string scope = null;
string prefix = null;
ParameterInfo[] formalParams = null;
object[] actual = null;
var methodCall = method.Body as MethodCallExpression;
if (methodCall != null)
{
scope = methodCall.Method.DeclaringType.FullName;
prefix = methodCall.Method.Name;
IEnumerable<Expression> actualParams = methodCall.Arguments;
actual = actualParams.Select(GetValueOfParameter<T>).ToArray();
formalParams = methodCall.Method.GetParameters();
}
else
{
// TODO: Check if the supplied expression is something that makes sense to evaluate as a method, e.g. MemberExpression (method.Body as MemberExpression)
var objectMember = Expression.Convert(method.Body, typeof (object));
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
var m = getter();
var m2 = ((System.Delegate) m);
var delegateDeclaringType = m2.Method.DeclaringType;
var actualMethodDeclaringType = delegateDeclaringType.DeclaringType;
scope = actualMethodDeclaringType.FullName;
var ar = m2.Target;
formalParams = m2.Method.GetParameters();
//var m = (System.MulticastDelegate)((Expression.Lambda<Func<object>>(Expression.Convert(method.Body, typeof(object)))).Compile()())
//throw new ArgumentException("Caller is not a method", "method");
}
// null list of paramMembers should disregard all parameters when creating key.
if (paramMembers != null)
{
for (var i = 0; i < formalParams.Length; i++)
{
var par = formalParams[i];
// empty list of paramMembers should be treated as using all parameters
if (paramMembers.Length == 0 || paramMembers.Contains(par.Name))
{
var value = actual[i];
keys.Add(par.Name, value.ToString());
}
}
if (paramMembers.Length != 0 && keys.Count != paramMembers.Length)
{
var notFound = paramMembers.Where(x => !keys.ContainsKey(x));
var notFoundString = string.Join(", ", notFound);
throw new ArgumentException("Unable to find the following parameters in supplied method: " + notFoundString, "paramMembers");
}
}
return scope + "¤" + prefix + "¤" + Flatten(keys);
}
private static object GetValueOfParameter<T>(Expression parameter)
{
LambdaExpression lambda = Expression.Lambda(parameter);
var compiledExpression = lambda.Compile();
var value = compiledExpression.DynamicInvoke();
return value;
}
}
然后,我有以下测试,可以正常工作:
[Test]
public void GetKey_From_Expression_Returns_Expected_Scope()
{
const string expectedScope = "MethodNameTests.DummyObject";
var expected = expectedScope + "¤" + "SayHello" + "¤" + MethodKey.Flatten(new Dictionary<string, string>() { { "name", "Jens" } });
var dummy = new DummyObject();
var actual = MethodKey.GetKey(() => dummy.SayHello("Jens"), "name");
Assert.That(actual, Is.Not.Null);
Assert.That(actual, Is.EqualTo(expected));
}
但是,如果我将 () => dummy.SayHello("Jens") 调用放入变量中,调用将失败。因为我在 GetKey 方法中不再得到MethodCallExpression,而是得到FieldExpression(MemberExpression 的子类。测试是:
[Test]
public void GetKey_Works_With_func_variable()
{
const string expectedScope = "MethodNameTests.DummyObject";
var expected = expectedScope + "¤" + "SayHello" + "¤" + MethodKey.Flatten(new Dictionary<string, string>() { { "name", "Jens" } });
var dummy = new DummyObject();
Func<string> indirection = (() => dummy.SayHello("Jens"));
// This fails. I would like to do the following, but the compiler
// doesn't agree :)
// var actual = MethodKey.GetKey(indirection, "name");
var actual = MethodKey.GetKey(() => indirection, "name");
Assert.That(actual, Is.Not.Null);
Assert.That(actual, Is.EqualTo(expected));
}
Dummy 类 SayHello 方法定义很简单:
public class DummyObject
{
public string SayHello(string name)
{
return "Hello " + name;
}
public string Meet(string person1, string person2 )
{
return person1 + " met " + person2;
}
}
我有两个问题:
- 有没有办法将变量
indirection发送到MethodKey.GetKey,并将其作为MethodCallExpression类型获取? - 如果没有,如果我得到
MemberExpression,如何获取提供的方法的名称和值?我在代码的“else”部分尝试了一些位,但没有成功。
感谢任何帮助。
先谢谢了,很抱歉发了这么长的帖子。
【问题讨论】:
-
Func<T>表示一个输入参数为零的函数。如果您将其更改为Func<R,I>或Func<R,I1,I2>或类似名称,您是否看到参数类型发生了变化?
标签: c# dynamic lambda expression-trees func