Мы имеем следующие конструкции в нашем коде, используется для обеспечения конкретного ресурса утилизируется после использования:Есть ли способ сообщить компилятору, что конкретный блок кода всегда будет выполняться?
using (var disposableThing = DisposableThing.Begin())
{
// do something
disposableThing.Finish(); // must always be called
}
Вот пример его использования:
List<int> ids;
using (var disposableThing = DisposableThing.Begin())
{
ids = disposableThing.GetSomeIds();
disposableThing.Finish();
}
DoSomethingElseWith(ids);
Поскольку эта модель настолько распространена мы написали метод на DisposableThing
инкапсулировать его:
static void ExecuteWithFinish(Action<DisposableThing> action)
{
using (var disposableThing = Begin())
{
action(disposableThing);
disposableThing.Finish();
}
}
, который позволяет нам переписать второй образец, как:
// #4
List<int> ids;
DisposableThing.ExecuteWithFinish(disposableThing =>
{
ids = disposableThing.GetSomeIds();
});
DoSomethingElseWith(ids); // compiler error "Use of unassigned local variable 'ids'"
Но компилятор отказывается компилировать этот код, поскольку он не имеет возможности узнать, что ids
всегда будет присвоен после ExecuteWithFinish
завершения (или брошено исключение, которое позволит предотвратить выполнение DoSomethingElseWith
так или иначе).
- Я знаю, что я мог бы добавить перегрузку
ExecuteWithFinish
, которая возвращает значения из сдавших вFunc
, что некрасиво. - Я знаю, что я мог бы подкласса
DisposableThing
и переопределить его методDispose
для вызоваFinish
, который является более чистым, опрятным и более быстрым способом, чем создание делегата каждый раз (это, вероятно, то, что я в конечном итоге сделаю).
Но для моего собственного назидания и в духе «что, если», можно ли проинформировать или даже обмануть компилятор, разрешив код в №4 как написанный?
редактировать: Да, я знаю, что я мог бы написать List<int> ids = null;
и обойти эту проблему полностью, но (а) я бы предпочел, чтобы не выполнять ненужные назначения (б) Я хотел бы, чтобы изменить код как можно меньше ,
Хочу отметить, что комментарий для 'disposableThing. Finish() 'says" всегда должен быть вызван ". Это верно, даже если «сделать что-то» вызывает исключение? Потому что если это так, вы должны инкапсулировать всю вещь 'Begin()' 'Finish()' 'Dispose()' в отдельный класс, который вы можете управлять с помощью одного 'use'. –
Просто общее замечание. Почему бы не использовать шаблон «IDisposable», который кажется более очевидным, чем повторное использование механизма с использованием метода «Готово»? Поместите код из «Готово» в свой метод «Dispose» (или просто вызовите 'Finish' из' Dispose'). Таким образом, вы избегаете ошибки компилятора и получаете поддержку от языка, потому что код в 'Finish' будет автоматически вызываться в конце инструкции using. –
@MatthewWatson: Из документации там я бы предположил, что 'Finish' будет вызываться в' Dispose' в любом случае. Я имею в виду, что вряд ли лучший вариант использования «IDisposable». В конце концов, потоки также автоматически закрываются при удалении, и в этом случае вам не нужно называть 'Close' вручную ... – Joey