Традиційно, навіть найпростіша програма на C# вимагала написання значної кількості "церемоніального" коду:
using System;
namespace Application
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Для виведення одного рядка потрібно було визначити namespace, клас та метод Main. Це створювало бар'єр для початківців та ускладнювало написання простих скриптів.
Розглянемо традиційну структуру програми та роль кожного компонента:
using System;
using System.Collections.Generic;
using System.Linq;
Призначення: Імпортують namespaces, щоб уникнути повної кваліфікації типів.
namespace MyApp
{
class Program
{
static void Main()
{
// Повна кваліфікація типу
System.Console.WriteLine("Hello");
System.Collections.Generic.List<int> numbers = new();
}
}
}
using System; using System.Collections.Generic;
namespace MyApp
{
class Program
{
static void Main()
{
// Короткі назви Console.WriteLine("Hello"); List<int> numbers = new(); }
}
}
using директиви поза namespace (C# 10+) для покращення читабельності та уникнення проблем з вкладеними namespace.namespace MyCompany.MyProduct.Utilities
{
// Класи, інтерфейси, структури
}
Мета: Організація типів у логічні групи та уникнення конфліктів імен.
Два стилі оголошення:
namespace MyCompany.MyProduct
{
class Calculator
{
// Вміст класу
}
class Logger
{
// Вміст класу
}
}
namespace MyCompany.MyProduct;
class Calculator
{
// Вміст класу
}
class Logger
{
// Вміст класу
}
// Без зайвих фігурних дужок!
class Program
{
// Члени класу
}
У традиційній структурі, клас Program служить контейнером для точки входу.
Метод Main — це спеціальний метод, який виконує CLR (Common Language Runtime) при запуску програми.
Сигнатури методу Main:
| Сигнатура | Опис | Використання |
|---|---|---|
static void Main() | Без параметрів, без повернення | Прості програми без аргументів |
static void Main(string[] args) | З аргументами командного рядка | Обробка параметрів запуску |
static int Main() | Повертає код завершення | Скрипти, CLI інструменти |
static int Main(string[] args) | Повний контроль | Професійні CLI застосунки |
static async Task Main() | Асинхронна точка входу (C# 7.1+) | Async/await у Main |
static async Task<int> Main(string[] args) | Асинхронна з поверненням | Async CLI з exit codes |
Приклад з аргументами:
using System;
namespace FileProcessor
{
class Program
{
static int Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Usage: FileProcessor <filename>");
return 1; // Exit code для помилки
}
string filename = args[0];
Console.WriteLine($"Processing file: {filename}");
// Логіка обробки файлу
return 0; // Успішне виконання
}
}
}
Виклик з командного рядка:
dotnet run -- myfile.txt
Main у всіх класах. Якщо знайдено більше одного Main, необхідно вказати головний клас через налаштування проєкту (<StartupObject>). CLR викликає цей метод для початку виконання програми.Базова структура класичного консольного проєкту:
З C# 9.0 (.NET 5+) з'явилася можливість писати код безпосередньо у файлі, без явного визначення namespace, класу та методу Main.
Раніше (C# 8 та нижче):
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
Тепер (C# 9+):
Console.WriteLine("Hello World!"); Всього один рядок! Компілятор автоматично генерує всю необхідну структуру.
Базова структура проєкту з Top-Level Statements:
Коли ви пишете:
using System;
Console.WriteLine("Hello World!");
Компілятор генерує еквівалент:
using System;
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
Магічна змінна args доступна автоматично:
// Program.cs
if (args.Length > 0)
{
Console.WriteLine($"Hello, {args[0]}!");
}
else
{
Console.WriteLine("Hello, World!");
}
dotnet run -- Alice
# Вивід: Hello, Alice!
if (args.Length == 0)
{
Console.WriteLine("Error: No arguments provided");
return 1; // Exit code
}
Console.WriteLine("Success");
return 0;
using System;
using System.Net.Http;
using System.Threading.Tasks;
// Await працює "із коробки"!
HttpClient client = new();
string content = await client.GetStringAsync("https://api.github.com");
Console.WriteLine($"Response length: {content.Length}");
Компілятор автоматично генерує:
static async Task Main(string[] args)
{
// Ваш код
}
Top-level statements мають бути на початку файлу, а потім можна визначати інші типи:
using System;
// Top-level statements
Console.WriteLine("Starting application...");
var calculator = new Calculator();
int result = calculator.Add(5, 3);
Console.WriteLine($"Result: {result}");
// Класи та інші типи після top-level коду
class Calculator
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
}
| Аспект | Класична структура | Top-level statements |
|---|---|---|
| Кількість рядків | 9+ для "Hello World" | 1 рядок |
| Точка входу | Явний Main метод | Неявний, згенерований |
args доступ | Параметр Main | Глобальна змінна args |
| Async/await | async Task Main | Підтримується автоматично |
| Кількість файлів | Необмежена | Тільки один файл з top-level |
| Підходить для | Великі проєкти | Скрипти, прототипи, навчання |
Починаючи з C# 10 та .NET 6, SDK автоматично додає типові using директиви залежно від типу проєкту.
У файлі .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
Для консольних застосунків (SDK: Microsoft.NET.Sdk):
// Ці using додаються автоматично:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Результат: Ви можете використовувати Console, List<T>, File, LINQ методи без явних using директив!
// Файл Program.cs (C# 10+, ImplicitUsings enabled)
// Всі типи доступні без using!
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var evenNumbers = numbers.Where(n => n % 2 == 0);
foreach (var num in evenNumbers)
{
Console.WriteLine(num);
}
Ви можете додати власні global using директиви, які будуть доступні у всіх файлах:
global using System.Text.Json;
global using MyCompany.SharedLibrary;
global using static System.Math;
Тепер у будь-якому файлі проєкту:
// JsonSerializer, PI, Sqrt доступні глобально!
double radius = 5;
double area = PI * Pow(radius, 2);
var json = JsonSerializer.Serialize(new { Radius = radius, Area = area });
Console.WriteLine(json);
GlobalUsings.cs для централізованого управління глобальними імпортами. Це покращує видимість залежностей проєкту.Структура проєкту з Global Usings:
Документування коду є критично важливим для підтримуваності та співпраці в команді.
// Це однорядковий коментар
int age = 25; // Вік користувача
/*
Це багаторядковий коментар.
Може займати кілька рядків.
Використовується для блоків тексту.
*/
int CalculateTotal(int a, int b)
{
return a + b;
}
/// <summary>
/// Обчислює суму двох цілих чисел.
/// </summary>
/// <param name="a">Перше число.</param>
/// <param name="b">Друге число.</param>
/// <returns>Сума a та b.</returns>
public int Add(int a, int b)
{
return a + b;
}
XML-коментарі використовуються інструментами для генерації документації (IntelliSense, DocFX, Sandcastle).
| Тег | Призначення | Приклад |
|---|---|---|
<summary> | Короткий опис типу/члена | <summary>Основний клас для розрахунків</summary> |
<remarks> | Детальний опис | <remarks>Цей клас використовує алгоритм...</remarks> |
<param> | Опис параметра методу | <param name="x">Координата X</param> |
<returns> | Опис значення, що повертається | <returns>Площа кола</returns> |
<exception> | Опис винятків | <exception cref="ArgumentNullException">Якщо параметр null</exception> |
<example> | Приклад використання | <example><code>var calc = new Calculator();</code></example> |
<see> | Посилання на інший тип/член | <see cref="Calculator.Add"/> |
<seealso> | Зв'язані елементи | <seealso cref="ICalculator"/> |
<paramref> | Посилання на параметр у тексті | Якщо <paramref name="x"/> менше нуля... |
<typeparam> | Опис generic параметра | <typeparam name="T">Тип елементу</typeparam> |
namespace MathLibrary;
/// <summary>
/// Надає математичні операції для геометричних розрахунків.
/// </summary>
/// <remarks>
/// <para>
/// Цей клас оптимізований для роботи з координатами в 2D-просторі.
/// Всі методи є thread-safe.
/// </para>
/// <para>
/// Для 3D розрахунків використовуйте <see cref="Geometry3D"/>.
/// </para>
/// </remarks>
public class Calculator
{
/// <summary>
/// Обчислює відстань між двома точками на площині.
/// </summary>
/// <param name="x1">X-координата першої точки.</param>
/// <param name="y1">Y-координата першої точки.</param>
/// <param name="x2">X-координата другої точки.</param>
/// <param name="y2">Y-координата другої точки.</param>
/// <returns>
/// Евклідова відстань між точками (<paramref name="x1"/>, <paramref name="y1"/>)
/// та (<paramref name="x2"/>, <paramref name="y2"/>).
/// </returns>
/// <exception cref="ArgumentException">
/// Викидається, якщо координати містять <see cref="double.NaN"/> або <see cref="double.PositiveInfinity"/>.
/// </exception>
/// <example>
/// <code>
/// var calculator = new Calculator();
/// double distance = calculator.Distance(0, 0, 3, 4);
/// Console.WriteLine(distance); // Вивід: 5
/// </code>
/// </example>
public double Distance(double x1, double y1, double x2, double y2)
{
if (double.IsNaN(x1) || double.IsNaN(y1) || double.IsNaN(x2) || double.IsNaN(y2))
{
throw new ArgumentException("Координати не можуть бути NaN.");
}
double dx = x2 - x1;
double dy = y2 - y1;
return Math.Sqrt(dx * dx + dy * dy);
}
/// <summary>
/// Обчислює площу кола за його радіусом.
/// </summary>
/// <param name="radius">Радіус кола. Має бути більше нуля.</param>
/// <returns>Площа кола.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Викидається, якщо <paramref name="radius"/> менше або дорівнює нулю.
/// </exception>
public double CircleArea(double radius)
{
if (radius <= 0)
{
throw new ArgumentOutOfRangeException(nameof(radius), "Радіус має бути додатнім.");
}
return Math.PI * radius * radius;
}
}
Увімкніть у .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
</Project>
Після компіляції буде створено XML-файл з усією документацією, який можна використати для генерації веб-сайту з документацією (наприклад, через DocFX).
Для великих проєктів можна використовувати тег <include>:
Файл: MathDocs.xml
<?xml version="1.0"?>
<doc>
<members>
<member name="M:Calculator.Add">
<summary>
Додає два числа разом.
</summary>
<param name="a">Перше число.</param>
<param name="b">Друге число.</param>
<returns>Сума a та b.</returns>
</member>
</members>
</doc>
Файл: Calculator.cs
public class Calculator
{
/// <include file='MathDocs.xml' path='doc/members/member[@name="M:Calculator.Add"]/*' />
public int Add(int a, int b)
{
return a + b;
}
}
Визначте, чи потрібна вам класична структура чи top-level statements:
Увімкніть сучасні фічі у .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project>
Типова структура сучасного консольного проєкту C#:
Створіть GlobalUsings.cs для спільних імпортів:
global using System.Text.Json;
global using Microsoft.Extensions.Logging;
Додавайте XML-коментарі до публічних API:
/// <summary>
/// Ваш опис
/// </summary>
public void MyMethod() { }
::
Створіть дві версії програми "Hello World":
Порівняйте кількість рядків коду.
// Очікуваний вивід для обох версій:
// Hello, World!
// Current time: [поточний час]
Напишіть програму з top-level statements, яка:
Console.ReadLine()dotnet run -- Олена
# Вивід: Привіт, Олена! Ласкаво просимо.
dotnet run
# Вивід: Введіть ваше ім'я: [користувач вводить]
# Вивід: Привіт, [ім'я]! Ласкаво просимо.
Перепишіть наступний код, використовуючи file-scoped namespace (C# 10+):
namespace MyCompany.Utilities
{
public class StringHelper
{
public string Reverse(string input)
{
char[] chars = input.ToCharArray();
Array.Reverse(chars);
return new string(chars);
}
}
}
Створіть програму з top-level statements, яка:
HttpClient для завантаження JSON даних з APIawait для асинхронного запиту// Використайте публічний API, наприклад:
// https://api.github.com/users/[username]
// Виведіть ім'я користувача та кількість публічних репозиторіїв
Створіть консольний проєкт з наступною структурою:
ImplicitUsings в .csprojGlobalUsings.cs з додатковими імпортами:
System.Text.JsonSystem.Text.RegularExpressionsusing директивСтворіть клас Calculator з методами:
Add(int a, int b) — додаванняSubtract(int a, int b) — відніманняMultiply(int a, int b) — множенняDivide(double a, double b) — діленняДодайте повну XML-документацію для кожного методу, включаючи:
<summary> — опис методу<param> — опис кожного параметра<returns> — опис значення, що повертається<exception> — опис винятків (для Divide приділенні на нуль)<example> — приклад використанняСтворіть консольний застосунок для обробки текстових файлів:
Вимоги:
count (кількість слів), upper (у верхній регістр), lines (кількість рядків)dotnet run -- myfile.txt count
# Вивід: Words: 150
dotnet run -- myfile.txt upper
# Вивід: [вміст файлу у верхньому регістрі]
dotnet run -- nonexistent.txt count
# Вивід: Error: File not found
# Exit code: 1

Створіть програму, яка демонструє комбінацію top-level statements з класами:
Структура:
Program.cs — точка входу та основна логікаUserManager — управління користувачами (список, додавання, пошук)ConsoleUI — методи для взаємодії з консоллюФункціонал:
List<User> (клас User з полями Name, Email, Age)// Приклад взаємодії:
// 1. Add user
// 2. Show all users
// 3. Find by name
// 4. Exit
// Choose option: 1
// Enter name: Іван
// Enter email: ivan@example.com
// Enter age: 25
// User added successfully!
Створіть невелику бібліотеку класів (Class Library project):
Вимоги:
MathOperations з методами для геометричних обчислень:
CircleArea(double radius) — площа колаRectanglePerimeter(double width, double height) — периметр прямокутникаTriangleArea(double baseLength, double height) — площа трикутника<example> секціямиНалаштування .csproj бібліотеки:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<DocumentationFile>bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml</DocumentationFile>
</PropertyGroup>
</Project>
Структура програми на C# пройшла значну еволюцію від ригідної, церемоніальної форми до гнучкого, мінімалістичного підходу. Розуміння обох підходів є критично важливим:
namespace → class → Main) забезпечує явну організацію та підходить для великих проєктів.Інтерактивна Консоль (Classic)
Повний гайд по роботі з System.Console API в C# - від базового вводу-виводу до створення інтерактивних меню з навігацією курсором
Змінні та Типи Даних
Вивчення змінних, констант, вбудованих типів даних, літералів, конверсії типів, різниці між типами значень і посилань, неявного типізування та бітових операцій у C#.