Лямбды C#: как *не* для задержки “разыменовывают”?

Я пытаюсь реализовать функциональность отмены с делегатами C#. В основном у меня есть UndoStack, который ведет список делегатов, реализующих каждую операцию отмены. Когда пользователь выбирает Edit:Undo, этот стек появляется от первого делегата и выполняет его. Каждая операция ответственна за продвижение соответствующего делегата отмены к стеку. Поэтому предположите, что у меня есть метод под названием "SetCashOnHand". Это похоже на это:

public void SetCashOnHand(int x) {
    int coh = this.cashOnHand;
    undo u = () => this.setCashOnHand(coh);
    this.cashOnHand = x;
    undoStack.Push(u);
}

Таким образом, этот метод создает делегата отмены, делает операцию, и (предположение, что это успешно выполняется), продвигает делегата отмены на UndoStack. (UndoStack достаточно умен это если undoStack. Нажатие называют в контексте отмены, делегат идет на стек повторного выполнения вместо этого.)

Моя проблема состоит в том, что это - немного раздражения для "кэширования" this.cashOnHand в coh переменную. Мне бы хотелось просто записать это:

undo u = () => this.setCashOnHand(this.cashOnHand);

Но конечно который не получит приведенную стоимость cashOnHand; это задерживает поиск значения, пока делегата не звонят, таким образом, код завершает выполнение ничего. Есть ли какой-либо способ, которым я могу "разыменовать" cashOnHand при построении делегата кроме наполнения значения в локальную переменную как coh?

Я действительно не интересуюсь слушанием о лучших способах сделать Отмену. Думайте об этом как об универсальном вопросе о делегатах с Отменой просто пример для создания вопроса более ясным.

9
задан Paul A Jungwirth 15 February 2010 в 10:50
поделиться

5 ответов

В общем, нет идеального решения, кроме того, что вы уже делаете.

Однако в вашем конкретном случае вы можете создать currier , например:

static Action Curry(Action<T> method, T param) { return () => method(param); }

Вы можете использовать его так:

Curry(setCashOnHand, this.cashOnHand);

Если ваш метод принимает другие параметры, вы можете использовать его как это:

Curry(cashOnHand => setCashOnHand(3, cashOnHand), this.cashOnHand);
6
ответ дан 3 November 2019 в 03:47
поделиться

На самом деле нет другого волшебного способа сделать то, что вы хотите. Ссылка оценивается при выполнении лямбда, а не раньше. Не существует автоматического способа «перехватить» значение, которое закрывается при создании лямбды, кроме того, что вы делаете.

0
ответ дан 3 November 2019 в 03:47
поделиться

Что ж, если на ваш стек отмены ссылается только экземпляр, который будет отменять свои собственные действия, тогда это не имеет значения.

Например:

public class MyType
{

  private UndoStack undoStack {get;set;}

  public void SetCashOnHand(int x) {
    int coh = this.cashOnHand;
    undo u = () => this.setCashOnHand(coh);
    this.cashOnHand = x;
    undoStack.Push(u);
   }
}

, тогда не имеет значения, на что ссылается UndoStack. Если вы вводите UndoStack и ссылки на этот экземпляр хранятся где-то еще, это будет иметь значение.

Если верно второе, я бы занялся рефакторингом этого, а не пытался придумать какую-то слабую ссылку или другие ухищрения, чтобы предотвратить утечку экземпляров через стек отмены.

Я полагаю, что в вашем случае на каждом уровне должен быть свой стек отмены. Например, на уровне пользовательского интерфейса должен быть стек отмены, который выскакивает из экземпляра, который необходимо отменить в следующий раз. Или даже группы экземпляров, которые необходимо отменить, поскольку для одного щелчка пользователя может потребоваться, чтобы несколько экземпляров разных типов прошли процедуру отмены по порядку.

0
ответ дан 3 November 2019 в 03:47
поделиться

Делегат работает в будущем .. Я думаю, что временная переменная, вероятно, лучший способ справиться с «запускать код сейчас, а не в будущем». Однако я думаю, вы могли бы написать функцию Bind (), которая связывает текущее значение с делегатом.

Action Bind<T>(Func<T> f, T value) { 
    return (Action)(() => f(value));
}
undoStack.Push(Bind(this.setCashOnHand, this.cashOnHand));
0
ответ дан 3 November 2019 в 03:47
поделиться

Нет, вам нужно будет захватить значение вашей переменной экземпляра вне лямбда-выражения, если вы хотите, чтобы оно оценивалось в это конкретное время.

Это ничем не отличается от того, если бы вы написали это:

public void SetCashOnHand(int x) {
    this.cashOnHand = x;
    undoStack.Push(UndoSetCashOnHand);
}

private void UndoSetCashOnHand()
{
    this.setCashOnHand(this.cashOnHand);
}

Лямбда-синтаксис лишь немного сбивает с толку, поскольку оценка кажется частью тела объявления метода, хотя на самом деле это часть автоматически сгенерированной частной функции, которая оценивается так же, как и любая другая функция.

Обычно люди решают эту проблему, используя параметризованную функцию и сохраняя значение, которое передается функции как часть стека. Если, однако, вы хотите использовать функцию с параметром less , вам придется захватить ее в локальном режиме.

2
ответ дан 3 November 2019 в 03:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: