说我有以下代码:
void Main()
{
SeveralCalls(() => CallWithParams("test"),
() => CallWithParams("AnotherTest"));
}
public void SeveralCalls(params Action[] methodsToCall)
{
foreach (var methodToCall in methodsToCall)
{
methodToCall();
}
}
public void CallWithParams(string someValue, string otherValue = null)
{
Console.WriteLine("SomeValue: " + someValue);
Console.WriteLine("OtherValue: " + otherValue);
}
是否可以通过仅修改otherValue
方法为CallWithParams
调用提供参数SeveralCalls
的值?
我希望通过SeveralCalls
方法为调用注入一个值。
作为一个背景,我正在研究调用tabled paramed存储过程的代码(作为将我的WCF服务集成到遗留代码中的一种方式)。该调用通常与数据库建立自己的连接,但我需要能够在事务中对多个调用进行分组。
如果我这样做,那么我需要每次调用使用相同的SqlConnection对象。 SeveralCalls
方法允许我将调用组合在一起,启动事务,并且(希望)将连接传递给实际调用sproc的方法。
答案 0 :(得分:2)
不,我不相信这是可能的。 () => CallWithParams("test")
被编译为调用CallWithParams("test", null)
的代码,其中两个值都是硬编码的。在SeveralCalls
中无法修改此内容(此外,可能还有一些复杂的反射和/或IL发射)。
如果你也可以修改Main
,这可能是一个很好的方法:
void Main()
{
SeveralCalls("Some other string",
otherValue => CallWithParams("test", otherValue),
otherValue => CallWithParams("AnotherTest", otherValue));
}
public void SeveralCalls(string otherValue, params Action<string>[] methodsToCall)
{
foreach (var methodToCall in methodsToCall)
{
methodToCall(otherValue);
}
}
public void CallWithParams(string someValue, string otherValue = null)
{
Console.WriteLine("SomeValue: " + someValue);
Console.WriteLine("OtherValue: " + otherValue);
}
或者:
string otherValue = null;
void Main()
{
SeveralCalls(() => CallWithParams("test", this.otherValue),
() => CallWithParams("AnotherTest", this.otherValue));
}
public void SeveralCalls(params Action[] methodsToCall)
{
this.otherValue = "Some other string";
foreach (var methodToCall in methodsToCall)
{
methodToCall();
}
}
// added static just to clarify that the otherValue here is separate from the
// one in 'this'
public static void CallWithParams(string someValue, string otherValue = null)
{
Console.WriteLine("SomeValue: " + someValue);
Console.WriteLine("OtherValue: " + otherValue);
}
答案 1 :(得分:2)
您可以使用Expression
s执行此操作。您目前正在使用Action
的任何地方,请改用Expression<Action>
。然后,您可以检查表达式对象,创建一个新对象并使用新表达式而不是初始表达式。这是一个例子。请注意ModifyExpression
方法,该方法验证表达式是否为调用CallWithParams
方法的lambda。在这个例子中,我正在查看参数,如果第二个缺失或为null,我以编程方式创建一个新的lambda表达式,第二个参数等于“重写值”。请注意,我必须将null
值添加到CallWithParams
lambda中。显然你不能使用带有默认参数的表达式,所以我只需要在lambdas中给它默认值。
static void Main()
{
SeveralCalls(() => CallWithParams("test", null),
() => CallWithParams("AnotherTest", null));
}
public static void SeveralCalls(params Expression<Action>[] expressions)
{
foreach (var expression in expressions)
{
var modifiedExpression = ModifyExpression(expression);
var action = modifiedExpression.Compile();
action();
}
}
private static Expression<Action> ModifyExpression(Expression<Action> expression)
{
var lambda = expression as LambdaExpression;
if (lambda == null)
return expression;
var call = lambda.Body as MethodCallExpression;
if (call == null)
return expression;
var method = typeof(Program).GetMethod("CallWithParams");
if (call.Method != method)
return expression;
if (call.Arguments.Count < 1 || call.Arguments.Count > 2)
return expression;
var firstArgument = call.Arguments[0];
var secondArgument = (call.Arguments.Count == 2 ? call.Arguments[1] : null);
var secondArgumentAsConstant = secondArgument as ConstantExpression;
if (secondArgumentAsConstant == null || secondArgumentAsConstant.Value == null)
secondArgument = Expression.Constant("overridden value");
var modifiedCall = Expression.Call(method, firstArgument, secondArgument);
var modifiedLambda = Expression.Lambda<Action>(modifiedCall);
return modifiedLambda;
}
public static void CallWithParams(string someValue, string otherValue = null)
{
Console.WriteLine("SomeValue: " + someValue);
Console.WriteLine("OtherValue: " + otherValue);
}
答案 2 :(得分:1)
没有。您Action
方法收到的SeveralCalls()
个对象不透明。您无法发现他们的调用代码恰好在不使用反射和CIL检查和反汇编的情况下调用CallWithParam()
。
(这样做会相当复杂,也可能无法保证可移植,因为C#编译器如何将lambda转换为匿名类型的细节不能保证不会改变。换句话说,只有通过假设才有可能C#编译器的非公开实现细节。)
更好的解决方案可能是将CallWithParams()
方法放在包含可以更改的字段或属性的类中。然后,您可以根据需要设置此字段/属性,以后对CallWithParams()
的任何调用都将相应地执行。这种可行性/健全性可能取决于你真正想要实现的目标。
在我看来,想要为lambdas注入一个参数表明设计有缺陷,所以如果你能分享更多关于你为什么要这样做的细节,也许我们可以帮助找到更好的解决方案。
答案 3 :(得分:1)
不,您的方法接受通用Action
。它无法确保它所调用的代码接收一个或两个参数或该参数的类型。
您可以在Action<string>
- 方法中接受SeveralCalls
,然后在调用中传递该值:
void Main()
{
SeveralCalls(extra => CallWithParams("test", extra),
extra => CallWithParams("AnotherTest", extra));
}
public void SeveralCalls(params Action<string>[] methodsToCall)
{
foreach (var methodToCall in methodsToCall)
{
methodToCall("some other param");
}
}
public void CallWithParams(string someValue, string otherValue = null)
{
Console.WriteLine("SomeValue: " + someValue);
Console.WriteLine("OtherValue: " + otherValue);
}