Существует ли синтаксис использования / удаления, который будет стремиться утилизировать, но такой же безопасный, как цепочка? - PullRequest
0 голосов
/ 08 ноября 2018

Если я делаю следующее:

using (var foo = bar.GetFoo())
using (var blat = new Blat(foo))
using (var bonk = Bonk.FromBlat(blat))
using (var bork = Bob.FromBip(bonk))
{
    var target = bork.ToArray(); // I GOT THE TARGET
}

Это очень безопасно и читаемо, но я понимаю, что только после самой последней строки все раскручивается, чтобы избавиться от всего.Учитывая, что foo, blat и bonk используются только в одной строке для создания bork, есть ли хороший способ («хорошо», будучи субъективным, конечно), чтобы избавиться от них в тот момент, когда они используютсяосвободить ресурсы как можно скорее?Мне очень нравится безопасность и удобочитаемость цепочки использования, мне просто любопытно, есть ли другой способ сделать это.

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Вы можете написать метод для использования IDisposable, который производит значение. Частично проблема заключается в том, что оператор using, будучи оператором, не производит значения, что затрудняет его цепочку:

public static TResult Use<TSource, TResult>(this TSource source, Func<TSource, TResult> selector)
    where TSource : IDisposable
{
    using (source)
        return selector(source);
}

Это позволяет вам написать:

var target = bar.GetFoo()
    .Use(foo => new Blat(foo))
    .Use(blat => Bonk.FromBlat(blat))
    .Use(bonk => Bob.FromBonk(bonk))
    .Use(bork => bork.ToArray());

Обратите внимание, что этот Use метод принимает IDisposable извне, но избавляется от него сам, поэтому, если вы вызовете Use для IDisposable, который все еще находится в области действия, вы можете в конечном итоге используя его даже после его удаления, что должно привести к ошибке. В этом случае мы никогда не держимся за одноразовые предметы, но кто-то еще может не знать, что им не следует этого делать.

Если вы хотите решить эту проблему, вы можете написать одну функцию, которая объединяет одноразовые значения. Объединяя все это в одну функцию, вы можете быть уверены, что никакие расходные материалы не «вытекут» из него так легко:

public static TResult ChainDisposables<T1, T2, T3, T4, TResult>(Func<T1> firstFunc,
    Func<T1, T2> secondFunc,
    Func<T2, T3> thirdFunc,
    Func<T3, T4> fourthFunc,
    Func<T4, TResult> resultFunc)
    where T1 : IDisposable
    where T2 : IDisposable
    where T3 : IDisposable
    where T4 : IDisposable
{
    return firstFunc()
        .Use(secondFunc)
        .Use(thirdFunc)
        .Use(fourthFunc)
        .Use(resultFunc);
}

(Вам нужно создать перегрузки для каждого различного числа объектов в цепочке, просто следуя примеру.)

Что позволит вам написать:

var target = ChainDisposables(() => bar.GetFoo(),
    foo => new Blat(foo),
    blat => Bonk.FromBlat(blat),
    bonk => Bob.FromBonk(bonk),
    bork => bork.ToArray());
0 голосов
/ 08 ноября 2018

Версии 1 и 2 НЕ являются функционально эквивалентными, поскольку в вашем псевдокоде порядок обращения должен быть обратным для 2:

Foo foo = null;
Blat blat = null;
Bonk bonk = null;
Bork bork = null;
try
{
    foo = bar.GetFoo();
    blat = new Blat(foo);
    bonk = Bonk.FromBlat(blat);
    bork = Bob.FromBip(bonk);
    var target = bork.Whatever; // I GOT THE TARGET
}
finally
{   // the disposal should happen in reverse order to instantiation
    bork?.Dispose();
    bonk?.Dispose();
    blat?.Dispose();
    foo?.Dispose();
}

И, увидев, как вы ошиблись в этом заказе, вы могли бы понять, почемуцепочечные операторы using гарантируют, что все происходит в правильном порядке, а не полагаетесь на то, что вы все сделаете правильно

Если компилятор может гарантировать, что все сделано в правильном порядке - попросите компилятор сделать это за вас- меньше вероятность ошибок

...