c# - C#中设置匿名函数的参数值

标签 c# expression-trees anonymous-methods

假设我有以下代码

private Func<T> _method;

public void SetExecutableMethod<T>(Func<T> methodParam)
{
    _method = methodParam;
}

public T ExecuteMethod(object[] parameterValues)
{
    //get the number of parameters _method has;
    var methodCallExpression = _method.Body as MethodCallExpression;
    var method = methodCallExpression.Method;
    ParameterInfo[] methodParams = method.GetParameters();

    //So i now have a list of parameters for the method call,
    //How can i update the parameter values for each of these?
    for (int i = 0; i < parameters.Count(); i++ )
    {
        methodParams[i] = ???''
    }

    return _method.Compile()();
}

public void InitAndTest()
{
    SetExecutableMethod( () => _service.SomeMethod1("param1 placeholder", "param2 placeholder") );

    T result1 = ExecuteMethod(new object[]{"Test1", "Test2"});
    T result2 = ExecuteMethod(new object[]{"Test3", "Test4"}););
}

在上面的代码中,我想为一些指向匿名函数的 Func 设置一个私有(private)变量,而不必再次设置它。 然后我希望能够使用不同的参数调用 ExecuteMethod(...) 。此方法应更新变量 _method 的参数值,然后调用该方法。 我可以很好地读取参数的数量及其值,我只是不确定如何设置这些参数的值?对此有什么想法吗?

最佳答案

这不是做这件事的方法。现在,你的 _method field 是 Func<T> 类型的代表,并且您期望它的 body 包含实际执行的另一个方法。这对您的来电者有很大的期望。我会忘记这种方法并寻找不同的东西。

一种方法是提供一个方法,该方法将对象数组作为其参数 ( Func<object[], T> ),然后使用适当的参数直接调用它(但绝不是其主体中的方法)。即使对于像 C# 这样的强类型语言,这种情况也不太常见,因为您会失去所有类型安全性(但话又说回来,您确实希望您正在设计的框架非常灵活)。

另一种方法是获得 MethodInfo实例,然后使用它的 Invoke方法。在某种程度上,这甚至可以更好地表达您的意图,因为很明显,可执行方法几乎可以执行任何操作。

接下来,您可以使用泛型来获得某种类型安全性,并要求将所有输入参数包装在一个参数类中。在那种情况下,你可能有一个强类型的 Func<Tparam, Tresult>方法,还有你的 Execute方法将接受 Tparam实例作为其参数。这将消除任何反射(reflection)的需要。

[编辑]

在我写的时候,我会尽量避免反射(reflection)。因为你写了你基本上需要方法结果的缓存,一个简单的方法可能是这样的:

  1. 为您的参数列表创建一个包装器,以便您可以“按值”比较它们。我添加了一个示例类,但您甚至可能希望允许传递 IEqualityComparer明确地,这样你就不必覆盖 Equals对于每个部分参数。

    // implements `IEquatable` for a list of parameters
    class Parameters : IEquatable<Parameters>
    {
        private readonly object[] _parameters;
        public Parameters(object[] parms)
        {
            _parameters = parms;
        }
    
        #region IEquatable<Parameters> Members
    
        public bool Equals(Parameters other)
        {
            if (other == null)
                return false;
    
            if (_parameters.Length != other._parameters.Length)
                return false;
    
            // check each parameter to see if it's equal
            // ...     
        }
    
        public override bool Equals(object obj)
        {
            return Equals(obj as Parameters);
        }
    
        public override int GetHashCode()
        { ... }
    
        #endregion
    }
    
  2. 为单个服务创建缓存。使用上面的包装器类,它应该简单地检查缓存结果是否存在:

    // contains cached results for a single service
    class CachedCallInfo
    {
        private readonly Func<object[], object> _method;
        private readonly Dictionary<Parameters, object> _cache
            = new Dictionary<Parameters, object>();
    
        public CachedCallInfo(Func<object[], object> method)
        {
            _method = method;
        }
    
        public T GetResult<T>(params object[] parameters)
        {
            // use out Parameters class to ensure comparison
            // by value
            var key = new Parameters(parameters);
            object result = null;
    
            // result exists?
            if (!_cache.TryGetValue(key, out result))
            {
                // do the actual service call
                result = _method(parameters);
    
                // add to cache
                _cache.Add(key, result);
            }
            return (T)result;
        }
    }
    
  3. 创建将按名称引用服务的最终类:

    public class ServiceCache
    {
        private readonly Dictionary<string, CachedCallInfo> _services =
            new Dictionary<string, CachedCallInfo>();
    
        public void RegisterService(string name, Func<object[], object> method)
        {
            _services[name] = new CachedCallInfo(method);
        }
    
        // "params" keyword is used to simplify method calls
        public T GetResult<T>(string serviceName, params object[] parameters)
        {
            return _services[serviceName].GetResult<T>(parameters);
        }
    }
    

您的缓存设置将如下所示:

serviceCache.RegisterService("ServiceA", @params => DoSomething(@params));
serviceCache.RegisterService("ServiceB", @params => SomethingElse(@params));

你可以简单地这样调用它:

var result = serviceCache.GetResult("ServiceA", paramA, paramB, paramC);

关于c# - C#中设置匿名函数的参数值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7732333/

相关文章:

delphi - 在 Delphi 2009 中类型转换匿名过程

c# - Predicate<int>匹配题

delphi - 匿名方法生成什么样的元数据?有没有办法删除它?

c# - CaSTLe.Windsor - 为什么名称需要与界面相似?

c# - 重置一个字符数组

c# - 将 Expression<Func<T,bool>> 转换为 Expression<Func<T1,bool>> 以便 T 成为 T1 的成员

IronPython 中质量评估表达式的性能

c# - 在 vb.net 中将一张图片更改为另一张 onclick

c# - PivotViewer 控件不显示图像 (Silverlight)

c# - 表达式树的性能