【问题标题】:replay a list of functions and parameters重放函数和参数列表
【发布时间】:2011-12-21 01:42:22
【问题描述】:

我有一系列功能,我想拥有以下功能。

  • 调用函数时,将自身添加到记住参数和值的函数列表中
  • 允许以后调用函数列表

不同的函数有各种不同的参数,我正在努力想出一种优雅的方法来做到这一点。任何帮助将不胜感激。

【问题讨论】:

  • 所有这些函数都具有相同的签名还是不同?你会用相同的参数调用这些函数还是会改变它们?
  • @JeffMercado 它们的不同之处在于参数和返回类型的数量可变。
  • 也许看看这个问题。 stackoverflow.com/questions/377217/… 这可能与您尝试做的事情不同,但它谈到了运行具有不同参数的委托。
  • 所以我一直在字里行间阅读,但你只是想创建一个类来促进记忆吗?它将缓存不同参数的返回值,而不是实际调用第二次方法。这是你想要做的还是你必须能够再次给他们打电话?
  • @JeffMercado 我确实想再次调用它们,它实际上旨在记录一系列函数,然后根据用户请求重播它们(可能多次)。

标签: c# function reflection delegates


【解决方案1】:

我认为这会满足您的需求,但是这些功能并不是“自行添加”。

public class Recorder
{
    private IList<Action> _recording;
    public Recorder()
    {
        _recording = new List<Action>();
    }
    public void CallAndRecord(Action action)
    {
        _recording.Add(action);
        action();
    }
    public void Playback()
    {
        foreach(var action in _recording)
        {
            action();
        }
    }
}

//usage
var recorder = new Recorder();
//calls the functions the first time, and records the order, function, and args
recorder.CallAndRecord(()=>Foo(1,2,4));
recorder.CallAndRecord(()=>Bar(6));
recorder.CallAndRecord(()=>Foo2("hello"));
recorder.CallAndRecord(()=>Bar2(0,11,true));
//plays everything back
recorder.Playback();

使函数“添加自己”的一种方法是使用 AOP 库,例如 postsharp 或 linfu 动态代理,并添加一个拦截器,将函数和参数添加到数组中。这样做可能比 IMO 值得做的工作更多,因为上面的内容要简单得多,并且仍然可以实现所需的功能。

【讨论】:

  • 如果变量作为参数传递给记录的函数,这个答案是有问题的。如果有问题的变量在调用CallAndRecord 和调用Playback 之间发生变化,则更改的值将用于回放。
  • @TreDubZedd 没错,在我的示例中,我使用了所有这不是问题的值类型,但如果使用引用类型,调用者需要小心地将传入的任何内容视为不可变的,否则副作用会随之而来。如果您想防止这种情况发生,您需要确保您的参数实现 ICloneable (或其他一些相等的克隆方法),然后遍历表达式并克隆所有参数,然后使用反射等进行所有调用。它变得非常复杂很快,但我认为这个解决方案是 OP 正在寻找的“物有所值”。
【解决方案2】:

对此几乎没有优雅的解决方案。既然你说这些方法都有不同的签名,那么就没有办法将它们存储在一个数组中作为委托。有了这个,接下来你可以尝试使用反射,将每个参数值存储在 object[] 中,将方法存储为 MethodInfo,然后调用它。

编辑:这是我能想到的:

    private Dictionary<MethodBase, object[]> methodCollection = new Dictionary<MethodBase, object[]>();

    public void AddMethod(MethodBase method, params object[] arguments)
    {
        methodCollection.Add(method, arguments);
    }

    private void MyMethod(int p1, string p2, bool p3)
    {
        AddMethod(System.Reflection.MethodInfo.GetCurrentMethod(), new object[] { p1, p2, p3 });
    }

    private void MyOtherMethod()
    {
        AddMethod(System.Reflection.MethodInfo.GetCurrentMethod(), new object[] { });
    }

然后用method.Invoke(method.ReflectedType, args)调用

【讨论】:

  • +1 这就是我想说的更多...但是使用 DynamicInvoke 来实现。不过,我以前从未真正使用过它,所以我可能离基地很远。
  • 要使用 DynamicInvoke,您需要 Delegate 实例,您需要为其提供 Type 和 MethodInfo,这样您就可以使用 Delegate.CreateDelegate() 创建委托,这基本上是相同的,只是多了几个步骤。你仍然需要反思。
【解决方案3】:

也许你可以知道如何使用Delegate.DynamicInvoke(Object[] obj) 函数。您可以将每个方法添加到一个对象数组中,然后遍历该数组,对每个方法调用 DynamicInvoke。

【讨论】:

    【解决方案4】:

    我不确定我是否理解您的问题,但我认为您可以使用指向函数的指针数组(在 C# 中称为委托)。因此,当调用函数时,将函数指针放入列表中。通过这种方式,您可以从列表中调用函数。这是一些想法。请注意,当您将新委托指针添加到列表 (functionPointers) 时,在第二个列表 myParameters 中添加类型为 Parameters 的新对象,该对象在名为 parameters 的公共属性中保存函数参数。这意味着参数列表functionPointers 中的委托imyParameters 列表中具有i-th 对象。这就是您如何知道哪些参数适用于哪个功能。可能有一些更好的解决方案,但这是替代方案。

    delegate void NewDelegate();
    class Parameter{
         public ArrayList parameters;
    
    }
    ArrayList functionPointers=new ArrayList();
    ArrayList<Parameter> myParameters=new ArrayList<Parameter>();
    NewDelegate myDelegate;
    
    void someFunction(int a, int b){
       myDelegate+=someFunction;//you add this function to delegate because this function is called
       functionPointers.add(myDelegate);//Add delegete to list
       Parameter p=new Parameter();//Create new Parameter object
       p.parameters.add(a);//Add function parameters
       p.parameters.add(b);
       myParameters.add(p);//add object p to myParameters list
    

    }

    【讨论】:

    • 这段代码甚至无法编译。您不能将任何方法存储到无参数委托。您必须遵循方法签名规则。
    【解决方案5】:

    您可以考虑使用操作或功能列表

    using System;
    using System.Collections.Generic;
    
    namespace ReplayConsole
    {
    class Program
    {
        private static IList<Action> _actions;
    
        static void Main(string[] args)
        {
            _actions = new List<Action>
                       {
                           () => {
                               //do thing 
                           },
                           () => {
                               //do thing 
                           },
                           () => {
                               //do thing 
                           },
                           () => {
                               //do thing 
                           },
                       };
    
            foreach (var action in _actions)
            {
                action();
            }
        }
    }
    

    如果您也想存储参数并且可以使用 Func 并以几乎相同的方式存储和使用它

    你也可以看看Tasks

    编辑:

    查看我在编写我的解决方案时弹出的答案,这个解决方案与 Brook 的非常相似

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-07
      • 2021-09-04
      • 2011-03-31
      • 1970-01-01
      • 2020-12-04
      • 1970-01-01
      • 1970-01-01
      • 2019-05-20
      相关资源
      最近更新 更多