#go #deferred
#Вперед #отложено
Вопрос:
Общий шаблон, который я использую, это:
resource.open()
defer resource.close()
иногда проверка ошибок между ними, что приводит к:
err := resource.open()
if err != nil{
//do error stuff and return
}
defer resource.close()
Иногда мне потребуется несколько открытых / закрытых ресурсов подряд, что приведет к повторению предыдущих 5 строк одна за другой. Это изменение может повторяться дословно несколько раз в моем коде (где мне нужны все те же ресурсы).
Было бы замечательно обернуть все это в функцию. Однако это приведет к закрытию ресурса, как только вызов функции завершится. Есть ли какой-либо способ обойти это — либо отложить «повышение уровня» стека вызовов, либо каким-либо другим способом?
Комментарии:
1. Вы не можете вызвать другую функцию, чтобы отложить что-либо. У вас может быть функция, которая инициализирует ресурсы и откладывает их закрытие, а затем вызывает другую функцию, которую вы передали в качестве аргумента с этими ресурсами.
2.
defer
выполняется, когда функция, в которой она вызывается, возвращает, точка. Повторение 5 тривиальных строк невероятно распространено в Go.3. @Adrian Верно. Жалобы на это — другая тема 🙂 Моя проблема заключается в том, что я повторяю одни и те же 20 тривиальных строк, где, скажем, мне нужны одни и те же четыре ресурса в нескольких разных местах.
4. Если вам потребуется открывать и закрывать один и тот же набор нескольких ресурсов много раз в вашем коде, это звучит как серьезная проблема с дизайном / архитектурой.
5. Нет, открытие / закрытие ресурсов — это нормально. Необходимость открывать / закрывать одну и ту же кучу ресурсов во многих местах вашего кода — это проблема дизайна.
Ответ №1:
Один из способов сделать это — использовать функцию «инициализатора» с обратным вызовом:
func WithResources(f func(Resource1, Resource2)) {
r1:=NewResource1()
defer r1.Close()
r2:=NewResource2()
defer r2.Close()
f(r1,r2)
}
func F() {
WithResources(func(r1 Resource1, r2 Resource2) {
// Use r1, r2
})
}
Сигнатура функции f
зависит от вашего конкретного варианта использования.
Другой способ — использовать структуру для набора ресурсов:
type Resources struct {
R1 Resource1
R2 Resource2
...
}
func NewResources() *Resources {
r:=amp;Resources{}
r.R1=NewR1()
r.R2=NewR2()
return r
}
func (r *Resources) Close() {
r.R1.Close()
r.R2.Close()
}
func f() {
r:=NewResources()
defer r.Close()
...
}
Комментарии:
1. имеет смысл, но кажется очень специфичным для подписи обратного вызова.
2. Отредактировано, чтобы включить другой метод.
Ответ №2:
Было бы замечательно обернуть все это в функцию.
Скорее всего, многим людям не понравилось бы читать такой код. Так что «замечательно» может быть очень субъективным.
Однако это приведет к закрытию ресурса, как только вызов функции завершится.
Точно.
Есть ли какой-нибудь способ обойти это […]?
Нет.
Комментарии:
1. итак, вы утверждаете, что проблемы нет, и я все выдумываю?
2. @WreckFish я ничего не утверждаю. Я констатирую истинный факт, что вы не можете изменить способ работы defer. (Это был ваш вопрос.)
3. Смотрите принятый ответ.