Dynami c метод обратного вызова - PullRequest
0 голосов
/ 26 апреля 2020

У меня есть ситуация, когда я l oop над элементами и назначаю метод "обратного вызова" на основе некоторых вычислений, в конце l oop этот метод вызывается (метод, который "выиграл" на основе приоритета и вычислений ).

Это очень просто в динамически типизированном языке, таком как JS, но, поскольку я новичок в типизированном языке, мне нужна помощь с методом обратного вызова динамического c.

Пожалуйста, прочитайте комментарии:

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);

Ответы [ 3 ]

1 голос
/ 26 апреля 2020

Спасибо за интересную проблему. Текущие ответы великолепны и предоставляют способ сделать то, что вы просили, с помощью 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;
    }
}

Эти два - из вашего примера, очевидно, это будет больше, чем просто один дочерний класс - представляют методы и их переменные в области видимости, которые мы хотим вызвать в конце. Логики выбора c, которые мы могли бы извлечь из чего-то вроде этого:

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();

Другая альтернатива, которую я видел, использовалась для Этот тип проблемы заключается в использовании конечного автомата. Это добавило бы еще большую сложность, но полезно, если ваша логика выбора c становится более сложной.

0 голосов
/ 26 апреля 2020
  float expensiveValue;
  Func<float> callback;
  if (MethodB(item, items[i], out expensiveValue)) {
    callback = () => {return expensiveValue;}
  }

  float result = callback.Invoke();

  // ..or..
  float expensiveValue;
  Func<float, float> callback = (val) => {return val;};
  if (MethodB(item, items[i], out expensiveValue)) {
    float result = callback.Invoke(expensiveValue);
  }
0 голосов
/ 26 апреля 2020

В c# вы можете хранить ссылки на методы, используя делегаты .

Сначала вы должны объявить свой делегат

delegate bool MyDelegate();

Затем вы можете объявить переменную с помощью тип этого делегата

MyDelegate myDelegate;

Теперь, если у вас есть метод, соответствующий этому делегату, например

static bool MyMethod() => true;

Вы можете назначить его так же, как это

myDelegate = MyMethod;

И затем используйте Invoke для вызова этого метода

bool b = myDelegate.Invoke();

В вашем случае методы принимают разные параметры, но все они возвращают bool, поэтому вы можете заключить их в лямбда-выражение , например,

myDelegate = () => MyMethod1(5);

Для метода, который принимает один int в качестве параметра, например, такой как ths

static bool MyMethod1(int i) => true;

Это консольное приложение решает, какой метод вызывать, основываясь на значении condition variable

delegate bool MyDelegate();

static void Main(string[] args)
{
    MyDelegate myDelegate;
    bool condition = false;

    if (condition) myDelegate = () => MyMethod1(5);
    else myDelegate = () => MyMethod2(7, "hello there");

    Console.WriteLine(myDelegate.Invoke());

    Console.ReadLine();
}

static bool MyMethod1(int i) => true;
static bool MyMethod2(int i, string s) => false;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...