2010-10-15 7 views
8

Я знаю, что это может показаться глупым, но почему следующий код работает, только если я закрыл() файл? Если я не закрываю файл, весь поток не записывается.Зачем мне закрывать() файл на C#?

Шаги:

  1. Выполнить этот код на форму нагрузки.
  2. Закрыть форму, используя мышь, как только она будет отображаться.
  3. Программа завершается.

Не должен ли файл-объект быть красным или закрытым автоматически, когда он выходит из сферы видимости? Я новичок в C#, но я привык добавлять вызовы в Close() в деструкторах C++.

// Notes: complete output is about 87KB. Without Close(), it's missing about 2KB at the end. 

// Convert to png and then convert that into a base64 encoded string. 
string b64img = ImageToBase64(img, ImageFormat.Png); 
// Save the base64 image to a text file for more testing and external validation. 
StreamWriter outfile = new StreamWriter("../../file.txt"); 
outfile.Write(b64img); 
// If we don't close the file, windows will not write it all to disk. No idea why 
// that would be. 
outfile.Close(); 
+5

Вы можете найти обсуждение на http://blogs.msdn.com/b/oldnewthing/archive/ 2010/08/10/10048150.aspx, чтобы помочь объяснить и обосновать различия между C# и C++. В общем, я бы сравнил деструкторы C++ с C# 'IDisposable' и' using {} '. И, конечно, 'использование' безопаснее, чем вызов' Close' или 'Dispose' явно, поскольку он завершает вызов в блоке' try-finally', чтобы гарантировать, что объект удален. – Brian

+0

Спасибо, все, за ясную и быструю помощь. – Harvey

ответ

21

C# не имеет автоматической детерминированной очистки. Вы должны обязательно вызвать функцию очистки, если вы хотите контролировать ее выполнение. Блок using является наиболее распространенным способом этого.

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

using (StreamWriter outfile = new StreamWriter("../../file.txt")) { 
    outfile.Write(b64img); 
} // everything is ok, the using block calls Dispose which closes the file 

EDIT: Как Харви отмечает, в то время как очистка будет предпринята попытка, когда объект получает собраны, это не какой-либо залог успеха. Чтобы избежать проблем с циклическими ссылками, среда выполнения не пытается завершить работу над объектами в «правильном» порядке, поэтому FileStream может быть уже мертв к моменту завершения финализатора StreamWriter и пытается сбросить буферизованный вывод.

Если вы имеете дело с объектами, нуждающимися в очистке, сделайте это явно, либо с помощью using (для использования на локальном уровне), либо путем вызова IDisposable.Dispose (для долгоживущих объектов, таких как референты членов класса).

+1

Как в сторону, в любое время, когда вы замечаете объект, который реализует 'IDisposable', вы должны использовать' use', если вы не хотите, чтобы объект быть очищенным сразу (например, если это переменная-член, вы можете захотеть «Утилизировать» ее позже). – Brian

+0

Представьте себе, что C++ должен очистить все ... scary – Spooks

+0

Я предполагаю, что меня поймало, что функция очистки ** никогда не вызвана для меня, если я не закрываю. Похоже, что когда я завершаю программу, вместо того, чтобы запускать очистку на любых объектах, нуждающихся в удалении, она просто заканчивается без сбора мусора. – Harvey

8

Поскольку Write() буферизуется, и буфер явно очищается Close().

+1

Или явным вызовом Flush(). –

+2

Что необходимо, чтобы внутренний дескриптор файла был закрыт, важнее ИМО. Тем более, что это может заблокировать файл до окончательного запуска финализатора. – CodesInChaos

1

Кэш операционной системы записывает в блочные устройства, чтобы обеспечить более высокую производительность ОС. Вы принудительно пишете путем промывки буфера после записи установки автозапуска для потокового блока.

+0

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

+1

Да, мой вопрос состоял в том, чтобы заставить писать, что вам не нужно закрывать – rerun

3

Потоки - это объекты, которые «управляют» или «обрабатывают» ненужные собранные ресурсы. Поэтому они (потоки) реализуют интерфейс IDisposable, который при использовании с «использованием» гарантирует, что собранные ресурсы, не связанные с мусором, будут очищены. попробуйте следующее:

using (StreamWriter outfile = new StreamWriter("../../file.txt")) 
{ 
    outfile.Write(b64img); 
} 

Без #Close вы не можете быть уверены в том, что основной дескриптор файла будет правильно закрыт. Иногда это может быть при отключении приложения.

+0

Спасибо, что помогает прояснить некоторую путаницу. В моем случае дескриптор файла ** никогда не закрывался даже при отключении приложения. (Закрыто в том смысле, что явный Close() будет скрыт) – Harvey

+0

приветствуется. – DevSolo

-2

Потому что дизайнеры C# клонировали Java, а не C++, несмотря на имя.

По-моему, они действительно пропустили лодку. Сбой стиля C++ на выходе области был бы намного лучше.

Даже не нужно освобождать память, чтобы быть лучше, просто запустите финализатор или метод IDisposable.

+2

-1: Жаловаться на ошибочный язык на самом деле не очень полезно, особенно жалуется, что «C# - это копия Java, и поэтому она работает как Java». Это было преднамеренное дизайнерское решение, и это связано с тем, как сборка мусора работает на C#. См. Http://blogs.msdn.com/b/oldnewthing/archive/2010/08/10/10048150.aspx для объяснения того, почему автоматическое удаление не будет работать. – Brian

+1

Я не тот, кто ниспровергнут, но в 6k rep, вы должны знать, как оставить комментарии к настоящему времени. Это не ответ. BTW, что вы хотите, существует, он называется C++/CLI, .NET с поддержкой RAII и автоматическими вызовами деструктора в конце области. –

+1

класс C {Поток; void M() {S s2 = OpenAStream(); this.s = s2; }} - Ваша идея состоит в том, что поток должен быть удален автоматически, когда s2 выходит за пределы области видимости, хотя у этого еще есть ссылка? Разве вы не думаете, что это было бы удивительно для людей? –

2

Потому что вы используете потоковый блокнот и не сбрасываете буфер до тех пор, пока не найдете Close() автора. Вы можете указать, что вы хотите, чтобы сценарист сбрасывался каждый раз, когда вы вызываете запись, установив для свойства streamer AutoFlush значение true.

Ознакомьтесь с документами. http://msdn.microsoft.com/en-us/library/system.io.streamwriter.aspx

Если вы хотите записать в файл без «закрытия», я хотел бы использовать:

System.IO.File 
Смежные вопросы