首页 > 解决方案 > 动态回调方法

问题描述

我有一种情况,我循环项目并根据一些计算分配一个“回调”方法,在循环结束时调用这个方法(基于优先级和计算“获胜”的方法)。

在像 JS 这样的动态类型语言中非常容易,但是因为我是类型语言的新手,所以我需要一些关于动态回调方法的帮助。

请阅读评论:

bool MethodA(MyClass item, List<MyClass> items) {
  int priority = 0;
  // here should be local variable for method,
  // for example in JS it would be "var func;"

  for (int i = 0; i < items.Count; i++) {
    if (priority <= 4) {
      float expensiveValue;
      if (MethodB(item, items[i], out expensiveValue)) {
        priority = 4;
        // assing a callback "func" that use "expensiveValue"
        // so that I don't have to calculate it again, e.g in JS: 
        /* func = function () {
          // use "expensiveValue", it's bound to this context
          return true;
        }; */
      }
    }
    if (priority <= 3) {
      float expensiveValue = MethodC(item, items);
      if (expensiveValue > 5f) {
        priority = 3;
        // same as in "if (priority <= 4)"
      }
    }
    // and other priority if's
  }

  // now that for loop is done, one of these callbacks 
  // was assigned to "func", in JS I'd call "return func();"
}

编辑:回调方法的几个例子

bool总是返回,但参数不同。

bool Method1(MyClass items[i], Vector3 expensiveValue);
bool Method2(MyClass items[i], float expensiveValue);

标签: c#

解决方案


感谢有趣的问题。当前的答案很棒,并提供了一种方法来完成您对 C# 的要求。我会建议一种替代方法,使代码更容易测试,对我来说,更清楚一点。您在 javascript 中所做的事情的关键是捕获局部范围的变量,这很酷,但也使这种类型的代码难以测试并且有点混乱。经常做的一件事是将这些变量提升到他们自己的类中。您可以对选择代码执行相同操作。例如:

class PriorityMethod
{
    public virtual bool Invoke()
    {
        return false; 
    }
}

class MethodPriorityB : PriorityMethod
{
    public MyClass FirstItem { get; set; }
    public MyClass SecondItem { get; set; }

    public float ExpensiveValue { get; set; }

    public override bool Invoke()
    {
        // use the properties to derive your result...
        return true;
    }
}

这两个-从您的示例中显然不仅仅是一个子类-代表我们最终要调用的方法及其作用域变量。我们可以这样提取选择逻辑:

class PriorityMethodSelector
{
    public PriorityMethod Create(ref int priority, MyClass first, MyClass second)
    {
        if (priority <= 4)
        {
            // do something to drive the expensive value
            return new MethodPriorityB() { FirstItem = first, SecondItem = second, ExpensiveValue = 1 };
        }

        return null;
    }

    public PriorityMethod Create(ref int priorty, MyClass first, IEnumerable<MyClass> items)
    {
        return null;
    }
}

然后我们把它们放在一起:

        PriorityMethodSelector selector = new PriorityMethodSelector();
        int priority = 0;
        PriorityMethod method = null;

        foreach(var item in items)
        {
            method = selector.Create(ref priority, special, item) ?? method;
            method = selector.Create(ref priority, item, items) ?? method;
        }

        if (null != method)
            method.Invoke();

我见过的用于此类问题的另一种替代方法是使用状态机。这会增加更多的复杂性,但如果您的选择逻辑变得更复杂,这将很有用。


推荐阅读