Я пытаюсь реализовать функциональность отмены с делегатами 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?
Я действительно не интересуюсь слушанием о лучших способах сделать Отмену. Думайте об этом как об универсальном вопросе о делегатах с Отменой просто пример для создания вопроса более ясным.
В общем, нет идеального решения, кроме того, что вы уже делаете.
Однако в вашем конкретном случае вы можете создать currier , например:
static Action Curry(Action<T> method, T param) { return () => method(param); }
Вы можете использовать его так:
Curry(setCashOnHand, this.cashOnHand);
Если ваш метод принимает другие параметры, вы можете использовать его как это:
Curry(cashOnHand => setCashOnHand(3, cashOnHand), this.cashOnHand);
На самом деле нет другого волшебного способа сделать то, что вы хотите. Ссылка оценивается при выполнении лямбда, а не раньше. Не существует автоматического способа «перехватить» значение, которое закрывается при создании лямбды, кроме того, что вы делаете.
Что ж, если на ваш стек отмены ссылается только экземпляр, который будет отменять свои собственные действия, тогда это не имеет значения.
Например:
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 и ссылки на этот экземпляр хранятся где-то еще, это будет иметь значение.
Если верно второе, я бы занялся рефакторингом этого, а не пытался придумать какую-то слабую ссылку или другие ухищрения, чтобы предотвратить утечку экземпляров через стек отмены.
Я полагаю, что в вашем случае на каждом уровне должен быть свой стек отмены. Например, на уровне пользовательского интерфейса должен быть стек отмены, который выскакивает из экземпляра, который необходимо отменить в следующий раз. Или даже группы экземпляров, которые необходимо отменить, поскольку для одного щелчка пользователя может потребоваться, чтобы несколько экземпляров разных типов прошли процедуру отмены по порядку.
Делегат работает в будущем .. Я думаю, что временная переменная, вероятно, лучший способ справиться с «запускать код сейчас, а не в будущем». Однако я думаю, вы могли бы написать функцию Bind (), которая связывает текущее значение с делегатом.
Action Bind<T>(Func<T> f, T value) {
return (Action)(() => f(value));
}
undoStack.Push(Bind(this.setCashOnHand, this.cashOnHand));
Нет, вам нужно будет захватить значение вашей переменной экземпляра вне лямбда-выражения, если вы хотите, чтобы оно оценивалось в это конкретное время.
Это ничем не отличается от того, если бы вы написали это:
public void SetCashOnHand(int x) {
this.cashOnHand = x;
undoStack.Push(UndoSetCashOnHand);
}
private void UndoSetCashOnHand()
{
this.setCashOnHand(this.cashOnHand);
}
Лямбда-синтаксис лишь немного сбивает с толку, поскольку оценка кажется частью тела объявления метода, хотя на самом деле это часть автоматически сгенерированной частной функции, которая оценивается так же, как и любая другая функция.
Обычно люди решают эту проблему, используя параметризованную функцию и сохраняя значение, которое передается функции как часть стека. Если, однако, вы хотите использовать функцию с параметром less , вам придется захватить ее в локальном режиме.