Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Created October 28, 2025 12:31
Show Gist options
  • Save sunmeat/da01c67559879ddd9cfc26270a25d719 to your computer and use it in GitHub Desktop.
Save sunmeat/da01c67559879ddd9cfc26270a25d719 to your computer and use it in GitHub Desktop.
finalizer example C#
using System.Text;
using System.IO;
namespace FinalizeExample
{
// фіналізація - механізм .net для автоматичного очищення некерованих ресурсів об'єкта під час GC.
// це перевизначення методу finalize() класу object, яке реалізується через деструктор ~class().
// навіщо: гарантує, що ресурси (файли, дескриптори, пам'ять) звільняються навіть якщо розробник забув викликати Dispose().
// яку проблему вирішує: витік ресурсів при несподіваному завершенні програми або помилках, коли Dispose не викликано.
// коли треба: при роботі з некерованими ресурсами (native api, файли через pinvoke, dll).
// коли ні: для чисто керованих об'єктів - gc сам очистить; фіналізація уповільнює gc (додатковий цикл, окремий потік).
// порада: уникайте використання фіналізаторів - використовуйте IDisposable з using для детермінованої очистки.
class FileHolder
{
public string FilePath { get; }
private FileStream? stream;
public int Id { get; }
public FileHolder(int id, string? tempFilePath = null)
{
Id = id;
FilePath = tempFilePath ?? Path.GetTempFileName();
// справжнє відкриття файлу з утриманням дескриптора (неуправляемий ресурс)
stream = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None);
byte[] data = Encoding.UTF8.GetBytes($"Дані об'єкта {id}\n");
stream.Write(data, 0, data.Length);
stream.Flush();
Console.WriteLine($"Створено та відкрито файл для об'єкта {Id}: {FilePath} (заблоковано дескриптором)");
}
// фіналізатор - викликається gc асинхронно в окремому потоці, не детерміновано.
~FileHolder()
{
Cleanup();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"Фіналізатор об'єкта {Id} викликано - дескриптор файлу {FilePath} закрито автоматично");
Console.Beep(800, 200); // звук для демонстрації виклику
}
private void Cleanup()
{
stream?.Dispose();
stream = null;
if (File.Exists(FilePath))
{
try
{
File.Delete(FilePath);
Console.WriteLine($"Файл об'єкта {Id} видалено в фіналізаторі");
}
catch (Exception ex)
{
Console.WriteLine($"Помилка видалення файлу {Id}: {ex.Message}");
}
}
}
}
class Program
{
static void Main()
{
Console.OutputEncoding = Encoding.UTF8;
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Демонстрація фіналізації в .NET - робота зі справжніми файлами\n" +
"--------------------------------------------------------------\n");
Console.ForegroundColor = ConsoleColor.White;
// створюємо 3 об'єкти з фіналізаторами - вони відкривають файли з дескрипторами
var holders = new FileHolder[3];
for (int i = 0; i < 3; i++)
{
holders[i] = new FileHolder(i + 1);
Console.WriteLine($"Об'єкт {i + 1} у поколінні {GC.GetGeneration(holders[i])}");
}
Console.WriteLine("\nФайли створено та заблоковано. Спроба видалення файлу об'єкта 1 (має провалитися):");
try
{
File.Delete(holders[0].FilePath);
Console.WriteLine("Видалено! (не повинно статися)");
}
catch (Exception ex)
{
Console.WriteLine($"Не вдалося видалити: {ex.Message} (файл заблоковано дескриптором)");
}
Console.WriteLine("\nНатисніть Enter для першого GC...");
Console.ReadLine();
// перший gc: об'єкти маркуються для фіналізації, але деструктори не викликаються
GC.Collect(0, GCCollectionMode.Forced, true);
Console.WriteLine("Перший GC завершено. Файли ще заблоковані, фіналізатори очікують.\n");
Console.WriteLine("Перевірте тимчасову папку - файли існують і заблоковані.\n");
Console.WriteLine("Натисніть Enter для другого GC (виклик фіналізаторів у фоновому потоці)...");
Console.ReadLine();
// другий gc: фіналізатори виконуються, дескриптори закриваються, файли видаляються
GC.Collect();
GC.WaitForPendingFinalizers(); // чекаємо завершення фіналізаторів
GC.Collect(); // збір для повного очищення
Console.WriteLine("Другий GC завершено. Фіналізатори викликані.\n");
// перевірка після фіналізації
Console.WriteLine("Спроба видалення файлу об'єкта 1 після GC (має не знайти файл):");
try
{
File.Delete(holders[0].FilePath);
Console.WriteLine("Видалено! (не повинно статися)");
}
catch (Exception ex)
{
Console.WriteLine($"Результат: {ex.Message} (файл уже видалено в фіналізаторі)");
}
Console.WriteLine("Перевірте тимчасову папку - файли видалено.\n");
Console.WriteLine("Кількість GC за поколіннями:");
for (int gen = 0; gen <= GC.MaxGeneration; gen++)
{
Console.WriteLine($"Покоління {gen}: {GC.CollectionCount(gen)}");
}
Console.WriteLine("\nПриклад показує, як фіналізація гарантує очищення дескрипторів і видалення файлів,\n" +
"але уповільнює процес. Краще використовувати IDisposable з using для детермінованої очистки.\n");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment