C# 更改动作对象实例的参数

C# 更改动作对象实例的参数,c#,.net,c#-4.0,.net-4.5,C#,.net,C# 4.0,.net 4.5,假设我有以下代码: void Main() { SeveralCalls(() => CallWithParams("test"), () => CallWithParams("AnotherTest")); } public void SeveralCalls(params Action[] methodsToCall) { foreach (var methodToCall in methodsToCall) {

假设我有以下代码:

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);
}
是否可以仅通过修改SeveralCalls方法为调用CallWithParams的参数otherValue提供值

如果通过SeveralCalls方法调用,我想在调用中注入一个值

作为一个背景,我正在编写调用表参数存储过程的代码,以将我的WCF服务集成到遗留代码中。调用通常会自行连接到数据库,但我需要能够在一个事务中对多个调用进行分组

如果我这样做,那么我需要每个调用使用相同的SqlConnection对象。SeveralCalls方法允许我将调用分组,启动一个事务,并希望将连接传递到实际将调用存储过程的方法。

否。SeveralCalls方法接收的操作对象是不透明的。如果不使用反射和CIL检查和反汇编,您将无法发现它们的调用代码恰好调用了CallWithParam

这样做相当复杂,而且也可能不能保证可移植,因为C编译器如何将lambda转换为匿名类型的细节不能保证不会更改。换句话说,只有假设C编译器的非公开实现细节,这才有可能实现

更好的解决方案可能是将CallWithParams方法放在包含可以更改的字段或属性的类中。然后,您可以根据需要设置此字段/属性,将来对CallWithParams的任何调用都将相应地进行。这一点的可行性/合理性可能取决于你真正想要实现的目标

在我看来,想要向lambdas中注入一个参数表明设计有缺陷,因此,如果您可以分享更多关于为什么要这样做的详细信息,也许我们可以帮助找到更好的解决方案。

否。SeveralCalls方法接收的动作对象是不透明的。如果不使用反射和CIL检查和反汇编,您将无法发现它们的调用代码恰好调用了CallWithParam

这样做相当复杂,而且也可能不能保证可移植,因为C编译器如何将lambda转换为匿名类型的细节不能保证不会更改。换句话说,只有假设C编译器的非公开实现细节,这才有可能实现

更好的解决方案可能是将CallWithParams方法放在包含可以更改的字段或属性的类中。然后,您可以根据需要设置此字段/属性,将来对CallWithParams的任何调用都将相应地进行。这一点的可行性/合理性可能取决于你真正想要实现的目标


在我看来,想要向lambdas中注入一个参数表明设计有缺陷,因此如果您能分享更多关于为什么要这样做的细节,也许我们可以帮助找到更好的解决方案。

不,我认为这是不可能的。=>CallWithParamstest被编译为调用CallWithParamstest、null的代码,这两个值都是硬编码的。除了潜在的复杂反射和/或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);
}

不,我认为这是不可能的。=>CallWithParamstest被编译为调用CallWithParamstest、null的代码,这两个值都是硬编码的。除了潜在的复杂反射和/或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);
}

否,您的方法接受泛型操作。它无法确保它调用的代码接收到一个或两个参数,或者该参数是什么类型的

您可以在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);
}

否,您的方法接受泛型操作。它无法确保它调用的代码接收到一个或两个参数,或者该参数是什么类型的

您可以在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);
}
你可以用它来做这件事。无论您当前在何处使用动作,请改用表达式。然后可以检查表达式对象,创建一个新对象,并使用新对象而不是初始表达式。这里有一个例子。请注意ModifyExpression方法,该方法验证表达式是否为调用CallWithParams方法的lambda。在本例中,我正在查看参数,如果第二个参数缺失或为null,我将以编程方式创建一个新的lambda表达式,其中第二个参数等于重写的值。注意,我必须将null值添加到CallWithParams lambdas中。显然,您不能使用带有默认参数的表达式,所以我只能在lam中为它指定默认值 bdas

你可以用它来做这件事。无论您当前在何处使用动作,请改用表达式。然后可以检查表达式对象,创建一个新对象,并使用新对象而不是初始表达式。这里有一个例子。请注意ModifyExpression方法,该方法验证表达式是否为调用CallWithParams方法的lambda。在本例中,我正在查看参数,如果第二个参数缺失或为null,我将以编程方式创建一个新的lambda表达式,其中第二个参数等于重写的值。注意,我必须将null值添加到CallWithParams lambdas中。显然,您不能使用带有默认参数的表达式,所以我只需要在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);
    }

是否要从操作中获取参数?请参见:是否要从操作中获取参数?看这个:使用表达式使这成为可能,但这仍然是一个可怕的设计实践。我同意。也许有更好的方法来做OP想要的事情。使用表达式可以做到这一点,但这仍然是一种可怕的设计实践。我同意。也许有更好的方法来满足OP的要求。