Отстъпване с IDisposable ресурс

Има ли подходящ начин за добив чрез ресурс за еднократна употреба? Върнатите обекти са IDisposable, но елементът, през който се повтаря, е.

Ето един пример:

public static IEnumerable<T> Fetch(IEnumerable<Guid> ids)
{
    using (var client = new CouchbaseClient())
    {
        yield return ids.Select(s => s.ToString());
    }
}

В момента извикването на това няма да изхвърли получения using ресурс. Знам, че мога просто към ToList и да го върна наведнъж, но има ли начин да се справя с това „правилно“ или трябва да запазя раздел на ресурса IDisposable и да го изхвърля ръчно, когато свърша?


person Jess    schedule 22.11.2012    source източник
comment
Не е ли отговорност на този, който ще повтори ресурса, да го изхвърли, когато е готов?   -  person Arthur Nunes    schedule 22.11.2012
comment
stackoverflow.com/q/2274664/34397   -  person SLaks    schedule 22.11.2012
comment
@ArthurNunes Подозирам, че е така, но просто съм любопитен дали езикът предлага нещо за това или трябва да го направя на ръка (вероятно като направя съдържащия клас IDisposable и преместя логиката за изхвърляне там).   -  person Jess    schedule 22.11.2012
comment
@SLaks Много интересен въпрос; радвам се, че го прочетох, но не съм сигурен доколко е подходящо за този въпрос. Имаше ли коментар или подточка, обсъждана там, която съм пропуснал? Ясно е, че маркираният отговор се получава от using и никой не го нарича лош, но никой не обяснява, че е добре (или защо е добре), доколкото мога да кажа.   -  person Servy    schedule 22.11.2012
comment
Бихте ли коригирали кода си, така че да се компилира? В момента вие предоставяте колекция, така че типът на връщането ще бъде IEnumerable<IEnumerable<string>>. Предполагам, че не това имахте предвид.   -  person svick    schedule 22.11.2012
comment
Вижте коментарите на stackoverflow.com/a/2274773/34397. yieldВлизане от using е лоша идея, тъй като викащите, които не Dispose() преброителят ще ви объркат.   -  person SLaks    schedule 22.11.2012
comment
@svick Не е това, което имах предвид, но беше чисто пример и без значение за въпроса.   -  person Jess    schedule 22.11.2012


Отговори (1)


Накратко, не е нужно да се тревожите за това, стига извикващият метода да обработва правилно обектите IEnumerator.

IEnumerator внедрява IDisposable и логиката, използвана при създаването на итераторните блокове, всъщност е достатъчно интелигентна, за да изпълни всички неизпълнени окончателни блокове, когато бъде изхвърлена. Блокът finally се създава в резултат на извикването using и там се изхвърля ресурсът IDisposable.

Така че докато IEnumerator обектите, създадени от този IEnumerable, са или итерирани напълно (в който случай последното MoveNext извикване ще достигне края на блока using и ще изхвърли ресурса), или изхвърлени, IDisposable клиентът ще бъде изхвърлен.

Обърнете внимание, че ако се притеснявате, че потребителят на вашия код може да не третира правилно обектите IEnumerator, тогава най-добре е да не използвате итераторен блок с мързелива оценка. Ако искате да сте сигурни, че дори и обаждащият се да не „играе добре“, тогава внимателно оценете метода (т.е. вземете кода, който имате, изхвърлете резултатите в списък и след това върнете този списък). Ако последствията от неизхвърлянето на ресурса са предимно или изцяло свързани с производителността (неосвобождаване на памет за известно време, поддържане на отворена връзка и т.н.), тогава това може да не е проблем, но ако задържането на ключалката завинаги е основен проблем (т.е. заключен ресурс, който може да доведе до блокиране, ако не бъде освободен), тогава предимството на мързелива оценка може да не си струва.

person Servy    schedule 22.11.2012
comment
Прав си, погрешно разбрах как добивът се справи с using, не осъзнавах, че ще го отваря всеки път, което изобщо не е това, което искам, но отговаря на въпроса ми. Просто ще разреша проблема, като създам екземпляр на моя базов клас, когато имам нужда от него. - person Jess; 22.11.2012
comment
@DmitryStarosta Итераторните блокове не бяха въведени до .NET 2.0, така че въпросът дори не се отнася за по-ранни версии. - person Servy; 23.11.2012
comment
@AndrewKoester Не го отваря всеки път; отваря го веднъж, когато поискате първия елемент, и го затваря, когато или поискате последния елемент, или изхвърлите IEnumerator. Както казах, ако е абсолютно 100% наложително ресурсът да бъде изхвърлен, не правете това. Ако като цяло е най-добре, но светът няма да свърши, ако кодът, използващ това, не работи добре, тогава е добре. - person Servy; 23.11.2012