Підрозділ 3.3
Клас Program та метод Main. Програми верхнього рівня
Пояснює метод Main як точку входу, програми верхнього рівня, неявний клас Program і правила розміщення типів у файлі.
3.3. Клас Program та метод Main. Програми верхнього рівня
Коли ви пишете перший рядок C#-коду і натискаєте «Запустити», виникає закономірне питання: з якого саме місця починається виконання програми? На відміну від скриптових мов, де інтерпретатор просто читає файл зверху донизу, у C# є чітко визначена точка входу — спеціальний метод, якому середовище виконання передає управління при старті. Розуміння цього механізму важливе, бо воно пояснює, чому одні змінні і методи доступні «скрізь», а інші — лише всередині класу, і яку роль відіграє структура файлу у великих проектах.
Метод Main як точка входу
Точкою входу до програми мовою C# є метод Main. Саме з нього починається виконання, і програма обов'язково повинна містити цей метод. Класично він виглядає так:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Клінічна система запущена.");
}
}Метод Main завжди оголошується зі статичним модифікатором static — це означає, що він належить класу, а не конкретному об'єкту, і може бути викликаний без створення екземпляра класу. Детальніше про static йтиметься в наступних розділах. Тип, що повертається, — void (метод нічого не повертає) або int (код завершення процесу). Параметр string[] args — масив рядків, через який можна передати аргументи при запуску програми з командного рядка.
Програми верхнього рівня
Починаючи з C# 9 і Visual Studio 2022, створення нового консольного проекту генерує набагато лаконічніший стартовий код:
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");Тут немає жодного визначення класу Program і методу Main — але програма повністю коректна і виконується без помилок. Це так звана програма верхнього рівня (top-level program), а рядки коду в ній — інструкції верхнього рівня (top-level statements).
Насправді компілятор C# сам генерує клас Program і метод Main, поміщаючи туди всі ваші інструкції верхнього рівня. Код вище фактично еквівалентний:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}Тобто розробник звільнений від написання цього шаблонного коду — компілятор робить це автоматично. Обидва варіанти дають той самий скомпільований результат.

Змінні, методи і класи у top-level програмах
Розміщення різних елементів у top-level програмі підпорядковується чітким правилам. Якщо ми оголошуємо змінні та локальні методи, вони потрапляють всередину згенерованого методу Main. Наприклад, ця програма верхнього рівня:
string greeting = "Ласкаво просимо до клінічної системи";
PrintGreeting(greeting);
void PrintGreeting(string message)
{
Console.WriteLine(message);
}є повністю еквівалентною такому явному запису:
class Program
{
static void Main(string[] args)
{
string greeting = "Ласкаво просимо до клінічної системи";
PrintGreeting(greeting);
void PrintGreeting(string message)
{
Console.WriteLine(message);
}
}
}Ситуація з класами інша: якщо в top-level програмі визначається новий клас, він розміщується поза класом Program, на рівні простору імен. Тому наступний код:
Patient patient = new("Іван Петренко", 45);
patient.PrintInfo();
class Patient
{
public string Name;
public int Age;
public Patient(string name, int age) { Name = name; Age = age; }
public void PrintInfo() => Console.WriteLine($"Пацієнт: {Name}, {Age} років");
}еквівалентний такому розгорнутому запису:
class Program
{
static void Main(string[] args)
{
Patient patient = new("Іван Петренко", 45);
patient.PrintInfo();
}
}
class Patient
{
public string Name;
public int Age;
public Patient(string name, int age) { Name = name; Age = age; }
public void PrintInfo() => Console.WriteLine($"Пацієнт: {Name}, {Age} років");
}Клас Patient знаходиться поза Program в обох випадках — top-level синтаксис лише приховує від нас шаблонний код Program.
Є одне важливе обмеження: визначення типів (класів, структур тощо) повинні йти в кінці файлу, після всіх інструкцій верхнього рівня. Якщо розмістити клас перед інструкціями — компілятор видасть помилку. Правильний порядок:
// Спочатку — інструкції верхнього рівня
Patient patient = new("Іван Петренко", 45);
patient.PrintInfo();
// Потім — визначення класів
class Patient
{
public string Name;
public int Age;
public Patient(string name, int age) { Name = name; Age = age; }
public void PrintInfo() => Console.WriteLine($"Пацієнт: {Name}, {Age} років");
}Явне визначення Main та його застосування
Незважаючи на зручність top-level синтаксису, явне визначення класу Program і методу Main залишається цілком коректним і досі широко використовується. Такий підхід обов'язковий у великих проектах, де потрібно мати кілька класів і чітко контролювати структуру коду, або коли інструменти і шаблони проекту вимагають явного Main.
Обидва підходи — top-level і явний Main — дають однаковий скомпільований результат. Вибір між ними — питання стилю, вимог команди або розміру проекту. Для навчальних прикладів і невеликих утиліт top-level синтаксис значно скорочує обсяг шаблонного коду і дозволяє зосередитись на суті задачі.
Асинхронна точка входу: async Task Main
У сучасних .NET-програмах часто виникає потреба виконувати асинхронні операції — звертатись до мережі, читати файли, робити запити до бази даних. Асинхронність у C# будується на ключових словах async і await. Щоб використовувати await безпосередньо в точці входу, метод Main може бути оголошений як асинхронний:
class Program
{
static async Task Main(string[] args)
{
await DoSomethingAsync();
}
}Тип, що повертається, змінюється з void на Task (або Task<int> якщо потрібен код завершення). Це дозволяє Main «чекати» на завершення асинхронних операцій, не блокуючи потік виконання.
У top-level програмах асинхронність вмикається ще простіше — достатньо використати await прямо в коді верхнього рівня, і компілятор автоматично зробить точку входу асинхронною:
using System.Net.Http;
var client = new HttpClient();
string result = await client.GetStringAsync("https://example.com");
Console.WriteLine(result);Компілятор перетворить цей код на async Task Main(...) самостійно. Детально асинхронне програмування розглядається в наступних розділах курсу — тут важливо розуміти, що точка входу програми повністю підтримує цей механізм.