在以下方法中,我想访问Action中包含的任何可选参数:
public static class ValidatorEngine
{
public static void Validate(Action someMethodWithOptionalArguments)
{
object target = someMethodWithOptionalArguments.Target;
}
}
所以如果我这样调用这个方法:
ValidatorEngine.Validate(() => UpdateByModel(model));
我希望能够访问传递给Action的模型参数。我甚至都不想调用这个Action。
我认为可以使用Action的Target属性完成某些操作,因为我可以在调试时看到模型。我无法以编程方式解决这个问题。
答案 0 :(得分:5)
如果你想检查但不执行它,这是Expression
的一个主要示例。只需将签名从Action
更改为Expression<Action>
即可。这将为您提供一个可以分析的表达式树。对于基本示例:
public static class ValidatorEngine
{
static void Main()
{
string model = "abc";
ValidatorEngine.Validate(() => UpdateByModel(model));
}
public static void Validate(Expression<Action> action)
{
var methodCall = action.Body as MethodCallExpression;
if (methodCall == null) throw new InvalidOperationException("Expected a method-call");
Console.WriteLine("Method: " + methodCall.Method.DeclaringType.Name + "." + methodCall.Method.Name);
var parameters = methodCall.Method.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
Console.WriteLine(parameters[i].Name + ": " + Evaluate(methodCall.Arguments[i]));
}
}
static object Evaluate(Expression exp)
{
switch (exp.NodeType)
{
case ExpressionType.Constant:
return ((ConstantExpression)exp).Value;
case ExpressionType.MemberAccess:
var me = (MemberExpression)exp;
switch (me.Member.MemberType)
{
case System.Reflection.MemberTypes.Field:
return ((FieldInfo)me.Member).GetValue(Evaluate(me.Expression));
case MemberTypes.Property:
return ((PropertyInfo)me.Member).GetValue(Evaluate(me.Expression), null);
default:
throw new NotSupportedException(me.Member.MemberType.ToString());
}
default:
throw new NotSupportedException(exp.NodeType.ToString());
}
}
static void UpdateByModel(object model) {
throw new NotImplementedException();
}
}
要支持更丰富的节点和方案,请参阅this richer version。
答案 1 :(得分:3)
如果Marc Gravell的详细答案不能满足您的需求,我能想到实现您所需要的唯一方法就是通过反思。在lambda闭包中捕获的状态变为编译器生成的类的公共字段(方便地与捕获的变量命名相同)。该类定义了一个包含lambda本身内容的方法,并将成为Action委托的方法。该类的一个实例将是委托的目标。
为简单起见,我将在此示例中使用C#4.0的动态关键字。根据您的需要,您可能希望使用“正确”反射来发现字段,这样您就不必事先知道它们的名称(加上某些字段是可选的)。
void Foo(Action action)
{
dynamic o = action.Target;
o.data = "ick";
action();
}
//...
string data = "ugh";
Foo(() => Console.WriteLine(data));
结果是“ick”被写入控制台。这就是我对这个解决方案的看法。除了感觉这只是渗透黑客,我们在这里使用的类型[CompilerGenerated]打了它的事实应该让你暂停:你和编译器之间没有契约。更高版本的编译器可以随意更改这一切在幕后的工作原理。最终,这种解决方案充其量是脆弱的。