Будет ли сначала вызываться Dispose и будет ли причиной сбой в случае отложенного выполнения? - PullRequest
4 голосов
/ 24 мая 2011

Существует два метода, один из которых возвращает данные с использованием LINQ в операторе using. Интересно, возможно ли, чтобы запрос выдал какое-то исключение, потому что выполнение запроса отложено, а используемая переменная уже удалена?

class Foo
{
    void Bar()
    {
       var bazResult = Baz();
       //... use bazResult here...
    }

    IEnumerable<int> Baz()
    {
        using (var d = new SomeDisposableSource())
        {
            return d.Select(e => e.Id);
        }
    }

}

Кстати, это должно было быть задано уже в какой-то форме, но я не могу найти очевидного кандидата. Так что не пинайте меня слишком сильно:)

Ответы [ 6 ]

4 голосов
/ 24 мая 2011

Я думаю, у вас будет исключение, если объект будет ликвидирован. Этот поток очень похож и дает несколько методов для решения проблемы. Самый простой - заставить выполнение, выполнив return d.Select(e => e.Id).ToList(), но это может не подойти вам

2 голосов
/ 24 мая 2011

Да, он может выдать исключение, но это зависит от реализации "SomeDisposableSource". Вы просите, чтобы источник получил IEnumerable или Array перед вызовом Dispose (), но вы фактически перечисляете каждый элемент после Dispose, поэтому, если он выдает исключение или нет, зависит от фактического кода для этого «возврата-возврата» код. (использует ли он какие-либо предметы?)

Вы можете обойти это (с более высоким использованием памяти), выполнив:

return d.Select(e => e.Id).ToArray();

Таким образом, все перечисления заканчиваются перед выполнением Dispose ().

РЕДАКТИРОВАТЬ: Использование:

return d.Select(e => e.Id).ToList();

... может быть и лучше.

0 голосов
/ 24 мая 2011

На самом деле выполнение не задерживается в вашем коде, потому что вы используете обычный return.Таким образом, метод Baz выполняется, возвращается и удаляется.Позже, когда вы будете перечислять результат, если этот механизм перечисления опирается на неуправляемые ресурсы, которые были удалены (что, скорее всего, имеет место в вашем примере), это не удастся.

Обходной путь прост: не предотвращайте отложенное выполнение с return, а используйте yield return.Это точное ключевое слово для отложенного выполнения.

Ваш метод становится таким:

    IEnumerable<int> Baz()
    {
        using (var d = new SomeDisposableSource())
        {
            //return d.Select(e => e.Id); //Baaaad ! No proper deferred execution
            foreach (var i in d.Select(e => e.Id)) yield return i; //Proper deferred execution
        }
    }

, и тогда все в порядке.using не вызывает метод Dispose до завершения перечисления.

0 голосов
/ 24 мая 2011

Вы смешиваете (более) детерминированный оператор using с (менее детерминированным) оператором LINQ. Оборачивая ресурс d в этот оператор using, вы явно заявляете, что к концу метода вы хотите, чтобы он был удален.

Поэтому, если вы хотите убедиться, что d утилизируется до выхода из метода, вам нужно будет немедленно выполнить LINQ с помощью ToArray, ToList или каким-либо другим методом этого сорта.

Путь намного сложнее , немного более сложный ( на комментатора ), будет состоять в создании пользовательского IEnumerable<T>, который позволит ресурсу (d) возвращаться с LINQ оператор и будет выполнен позднее, то есть вызывающий теперь отвечает за удаление IEnumerable<T> (обычно просто путем использования блока foreach).

0 голосов
/ 24 мая 2011

Я думаю, что Герардо находится на правильном пути, но я бы кодировал его немного по-другому, что могло бы привести к уменьшению объема памяти:

return d.Select(e => e.Id).ToList();

РЕДАКТИРОВАТЬ: Ой!IndigoDelta далеко впереди меня

0 голосов
/ 24 мая 2011

whether it's possible довольно странный вопрос.Да, это возможно.Ваш SomeDisposableSource может проверить, был ли он утилизирован или нет GetEnumerator методом.

...