#c# #delegates
#c# #делегирует
Вопрос:
У меня есть метод SaveChanges<T>(T object)
, который часто вызывается во всем моем коде, за исключением того, что в зависимости от действия, вызывающего метод, будет другой метод, вызываемый из SaveChanges. Что-то вроде этого…
protected void SaveChanges<T>(T mlaObject, SomeFunction(arg))
where T : WebObject
{
try { this._db.SaveChanges(); }
catch (Exception e)
{
Console.WriteLine("Error: " e);
SomeFunction(arg);
}
}
Примеры использования:
SaveChanges<MlaArticle>(article, article.Authors.Remove(person)) //person is an object of type MlaPerson
//OR
SaveChanges<MlaArticle>(article, article.RelatedTags.Remove(tag)) //tag is an object of type Tag
//OR
SaveChanges<MlaArticle>(article, article.RelatedWebObjects.Remove(location)) //location is an object of type MlaLocation
Я прочитал о методах делегирования, но я немного смущен тем, как реализовать это в соответствии с моими требованиями или если мои требования вообще требуют использования для делегатов.
РЕДАКТИРОВАТЬ: Кроме того, можно ли передать несколько действий?
Ответ №1:
Как насчет:
protected void SaveChanges<T>(T mlaObject, Action<T> rollback)
where T : WebObject
{
try { this._db.SaveChanges(); }
catch (Exception e)
{
Console.WriteLine("Error: " e);
rollback(mlaObject);
}
}
Вызывается как:
this.SaveChanges(myObj, x => article.Authors.Remove(x));
Теперь, после второго прочтения вашего вопроса, я не вижу смысла передавать mlaObject
, поскольку он никогда не используется.
// this.SaveChanges(
// () => article.Authors.Remove(author),
// () => article.RelatedTags.Remove(tag));
protected void SaveChanges(params Action[] rollbacks)
{
try { this._db.SaveChanges(); }
catch (Exception e)
{
Console.WriteLine("Error: " e);
foreach (var rollback in rollbacks) rollback();
}
}
// Overload to support rollback with an argument
// this.SaveChanges(
// author,
// article.Authors.Remove,
// authorCache.Remove);
protected void SaveChanges<T>(T arg, params Action<T>[] rollbacks)
{
try { this._db.SaveChanges(); }
catch (Exception e)
{
Console.WriteLine("Error: " e);
foreach (var rollback in rollbacks) rollback(arg);
}
}
Комментарии:
1. Вам нужно добавить параметр arg .
2. Мне это нравится, очень прямолинейно. Можно ли отправить несколько действий?
3. Конечно, вы могли бы использовать IEnumerable<Действие<T>> или само ваше действие<T> может указывать любое количество вызываемых объектов, например x => { doA(x); doB(x); doC(x); }
4. @GeorgeDuckett: спасибо, я интуитивно понял, что это так
mlaObject
. Но это может быть не так при втором чтении.5. @bflemi3: добавлена поддержка нескольких действий, удалено неиспользуемое использование
mlaObject
.
Ответ №2:
ОБНОВЛЕНИЕ Мне было немного непонятно из вашего вопроса, если переданный аргумент используется где-либо еще в методе, похоже, что это не так, поэтому вы можете просто взять Action
и использовать лямбда-выражение, чтобы указать делегата для вызова с захваченным аргументом:
protected void SaveChanges<T, TArg>(T mlaObject, TArg arg, Action undoFunction)
where T : WebObject
{
try { this._db.SaveChanges(); }
catch (Exception e)
{
Console.WriteLine("Error: " e);
undoFunction();
}
}
Которому вы можете передать:
SaveChanges(article, () => article.Authors.Remove(person));
Или, если это сам myObj, в этом случае (как уже ответили sixlettervariables) вы можете просто передать его обратно в делегате в соответствии с его кодом.
Или, отличается ли аргумент от mlaObject, и вы хотите также выполнять другие действия с ним в коде, и в этом случае вы могли бы сделать:
protected void SaveChanges<T, TArg>(T mlaObject, TArg arg, Action undoFunction)
where T : WebObject
{
try { this._db.SaveChanges(); }
catch (Exception e)
{
Console.WriteLine("Error: " e);
undoFunction(arg);
}
}
И затем иметь:
SaveChanges(article, person, article.Authors.Remove);
Комментарии:
1. Я надеюсь, что мне больше не придется работать с таким кодом без исходного кода или документации ;-).
2. Ну, в идеале у вас должно быть хотя бы несколько хороших /// комментариев к нему. Делегирование действий / функций быстро становится второй натурой, особенно когда вы начинаете играть с LINQ.
3. Этот код на самом деле совсем не так плох. Это может использовать что-то более сложное, чем вам нужно для простого приложения Notepad, но в реальных задачах будут использоваться многие функции языка. Это очень аккуратно и понятно. Вы недавно просматривали какой-нибудь код на C или C ?
4. Мой единственный вопрос с этим кодом заключается в том, какова полезность
T mlaObject
, если он не используется. Возможно, это не вопрос для @JamesMichaelHare, но, безусловно, для OP 🙂5. Очень хороший вопрос, если он не используется, то, конечно, его можно удалить, в противном случае я предполагал, что в нем будет какое-то использование, ведущее к this._db.SaveChanges() .
Ответ №3:
protected void SaveChanges<T,U>(T mlaObject, Action<U> action, U arg)
where T : WebObject
{
try { this._db.SaveChanges(); }
catch (Exception e)
{
Console.WriteLine("Error: " e);
action(arg);
}
}
Надеюсь, я правильно понял вопрос…
Комментарии:
1. да, вы все правильно поняли. можно ли отправить несколько действий?
Ответ №4:
Ваш SaveChanges
метод будет выглядеть примерно так:
protected void SaveChanges<T,TArg>(T mlaObject, TArg arg, Action<T,TArg> someFunction)
where T : WebObject
{
...
}
Вызывается как:
SaveChanges<MlaArticle,Person>(article,person, (article,person) => article.Authors.Remove(person))
Комментарии:
1. Вам нужно будет указать тип обоих общих аргументов, если вы укажете один из них.
Ответ №5:
если мои требования вообще требуют использования для делегатов.
Если вы хотите SaveChanges
, чтобы метод выполнял какую-либо функцию, у вас есть два варианта
- пусть он выполняет функцию напрямую (кодирует внутри метода или вызывает какой-либо второй метод изнутри метода); или
- предоставьте функцию
SaveChanges
методу в качестве делегата.
Когда использовать каждый из них — это выбор дизайна, который зависит от сценария, общего решения и ваших предпочтений.
Преимущества первого
- Возможность видеть все возможные результаты
SaveChanges
метода в одном месте - Менее запутанный для тех, кто не знает, как работают делегаты
Преимущества второго
- Возможность исключить все возможные функции из
SaveChanges
метода (это не требует огромногоcase
илиif else if else if
) - Функции, которые передаются
SaveChanges
методу, могут находиться над ним в стеке вызовов, ему не нужно знать, что это такое или как они работают, они могут делать вещи, которые он не понимает, и их можно использовать повторно — вызывать в другом месте или использовать в качестве делегатов в других функциях.
Я думаю, что первый пункт здесь является основным. Если вы обрабатываете только пару сценариев, тогда можно использовать an if else if else if
, но если вы получаете больше, чем несколько вариантов, и предпочитаете более общий SaveChanges
метод, тогда psdd этот делегат.
Ответ №6:
protected void SaveChanges<T>(T mlaObject, Action<T> functionToCall)
{
try { this._db.SaveChanges(); }
catch (Exception e)
{
Console.WriteLine("Error: " e);
functionToCall(mlaObject);
}
}
Вызовите так:
SaveChanges(actualArticle, article => article.Authors.Remove(person));
Я пропустил бит WebObject, поскольку он вообще не использовался в функции.