возврат одноразового в зависимости от одноразового использования родителей - PullRequest
0 голосов
/ 14 мая 2018

Вот часть кода, над которым я работаю (изменено для ясности):

public Stream getMyArchiveStream(string archivepath)
{
    using(var archive = ZipFile.OpenRead(_filepath))
    {
        var entry = archive.GetEntry(archivepath);
        return entry.Open();
    }
}

public void useMyArchiveStream()
{
    using(var myStream = getMyArchiveStream("test.path"))
    {
        //Do stuff
    }
}

Теперь это не работает, потому что архив находится на выходе из getMyArchiveStream , который предотвращает использование myStream .

Есть ли способ утилизации архива при утилизации myStream из?

Альтернатива состоит в том, чтобы оставить архив открытым и сделать содержащийся класс доступным, но у него есть свои недостатки в удобстве использования.

Справочная информация:

Я создал простой класс упаковки (по крайней мере, проще System.IO.Packaging), который возвращает файлы в виде байтовых массивов.Очевидно, что это занимает много памяти, и я хочу вместо этого использовать потоки.

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Проще говоря, вы берете ярлыки, которые вы просто не можете взять.Как буквально говорит заголовок вашего вопроса, вы пытаетесь использовать объект, который зависит от объекта, которым вы располагаете.

Что вы пытаетесь сделать, это открыть файл, прочитать некоторые информация о его внутренностях (не самих внутренних), , затем , закрытие файла, и , а затем попытка использовать эту информацию для фактического чтения из этого файла.Это невозможно; файл уже закрыт. Точно так же, как вы не можете вернуть одноразовый объект из его собственного блока using, не заканчивая удаленным и, таким образом, непригодным объектом, вы, очевидно, также не можете вернуть то, что зависит от одноразового предмета.

Итак, в целом весь мыслительный процесс, стоящий за вашей getMyArchiveStream функцией, несовершенен.Вы не должны иметь эту функцию вообще.Вам просто нужно настроить другую функцию следующим образом:

public void UseMyArchiveStream()
{
    using(var archive = ZipFile.OpenRead(_filepath))
    {
        var entry = archive.GetEntry("test.path");
        using(var myStream = entry.Open())
        {
            //Do stuff
        }
    }
}

Одна альтернатива - оставить archive открытым ... но, как прокомментировал mjwills , есть другой способ сделатьчто вы хотите, и это, чтобы дать UseMyArchiveStream Action<> или Func<> в качестве аргумента.Это буквально означает, что комментарий «Делать вещи» в приведенном выше коде заменяется вызовом любой функции, которую вы задаете в качестве аргумента:

public void UseMyArchiveStream(String zipPath, String entryName, Action<Stream, String> doStuff)
{
    using (var archive = ZipFile.OpenRead(zipPath))
    {
        var entry = archive.GetEntry(entryName);
        using (var myStream = entry.Open())
        {
            doStuff(myStream, entry.FullName);
        }
    }
}

Демонстрируется с функцией void SaveStreamToFile(Stream file, String filename):

UseMyArchiveStream(_filepath, "test.path", (str, nm) => SaveStreamToFile(str, nm));

С помощью Func<> вы можете сделать перегрузку, которая также возвращает значение.Последний аргумент внутри <> всегда является типом возвращаемого значения.Но вы можете легко использовать обобщения, чтобы они зависели от вызывающего ввода:

public T UseMyArchiveStream<T>(String zipPath, String entryName, Func<Stream, String, T> doStuff)
{
    using (var archive = ZipFile.OpenRead(zipPath))
    {
        var entry = archive.GetEntry(entryName);
        using (var myStream = entry.Open())
        {
            return doStuff(myStream, entry.FullName);
        }
    }
}

Вы можете вызывать это так же, только с функцией, которая возвращает значение, а не void.Демонстрируется с помощью Boolean DostuffWithFile(Stream file, String entryName):

Boolean ok = UseMyArchiveStream(_filepath, "test.path", (str, nm) => DostuffWithFile(str, nm));

Обратите внимание, что вызываемая функция не обязательно должна соответствовать точной сигнатуре аргумента.Таким способом вызова можно полностью заменить отсутствующие аргументы локальными данными.

Демонстрируется с Boolean DostuffWithFile(Stream file, String entryName, Boolean someOption, String outputFolder):

Boolean ok = UseMyArchiveStream(_filepath, "test.path", (str, nm) => DostuffWithFile(str, nm, true, _savePath));

Это будет работать до тех пор, пока ввод, который должен быть предоставленUseMyArchiveStream это просто часть перед =>.Конечно, вы можете манипулировать аргументами так, как хотите;Вы можете даже дать функции весь объект ZipArchiveEntry и, возможно, даже источник ZipFile, так что вы можете делать с ним все, что захотите.

Единственный недостаток этого подхода заключается в том, что вы можете 'На самом деле имена компонентов Action<> или Func<> не имеют, поэтому в этом случае вы не можете узнать только из сигнатуры функции UseMyArchiveStream, получит ли этот String аргумент, заданный Func<Stream, String, T>, entry.Name или entry.FullName.То же самое относится к предоставлению нескольких аргументов одного типа;если у вас есть пять логических опций в этом Func<>, вам может быть трудно вспомнить точно, что именно, без необходимости каждый раз заглядывать в код.Поэтому не забудьте точно документировать это в комментариях к функциям, чтобы избежать путаницы позже.

0 голосов
/ 14 мая 2018

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

Финализация - это гарантия того, что неуправляемые ресурсы (файловые дескрипторы, сетевые дескрипторы, собственно выделенное пространство памяти) могут быть освобождены ГХ.Но хотя он уверен, что GC будет работать, он не будет определен , когда это сделает.Действительно, если он запускается только при закрытии приложения, это идеальный случай, к которому стремится большинство реализаций.

Утилизация - это все, чтобы убедиться, что финализация детерминирована.Когда вы утилизируете, вы больше не завершаете.Вы делаете работу Завершения рано.Вы делаете это тогда, когда хотите, а не когда GC обходит это.

Есть много деталей, но для меня все сводится к двум случаям, когда вы реализуете IDisposeable: * Вы обрабатываете неуправляемый ресурс напрямую.В этом случае вы сначала делаете финализацию.Затем утилизируйте как функцию удобства / удобства использования.Вы действительно не столкнетесь с этим делом, так как большинство программистов позаботятся о нем.* Вы обрабатываете любой класс, который реализует IDisposeable, с единственной целью «ретранслировать» вызов Dispose во все связанные экземпляры.Легко 95% всех одноразовых вызовов об этом.

С ZipArchive мое предположение - главная причина, по которой он реализует IDisposeable, из-за файлового дескриптора, который он должен содержать.И Entry может просто реализовать его, потому что он содержит ссылку на экземпляр ZipArchive.И это должно таким образом ретранслировать вызов.Хотя, возможно, в ZipArchiveEntry есть что-то еще, что требует удаления, я думаю, что это маловероятно.Таким образом, вы должны быть в состоянии отключить это использование, если будете уверены, что экземпляр ZipArchive правильно удален.

...