C#: основы языка и приложение с интерфейсом
Синтаксис C#, свойства и автосвойства, коллекции и LINQ, обработка исключений, создание оконного приложения на Windows Forms и работа с базой данных.
C# выбирают для курсовых, где нужно быстро получить работающее приложение с оконным интерфейсом. Visual Studio позволяет собрать форму мышью, а на защите наглядная программа выигрывает у консольной.
Класс и свойства
public class Product
{
// Автосвойство — компилятор сам создаёт скрытое поле
public string Name { get; set; }
public int Quantity { get; set; }
private decimal price;
public decimal Price // свойство с проверкой
{
get => price;
set
{
if (value < 0)
throw new ArgumentException("Цена не может быть отрицательной");
price = value;
}
}
public decimal Total => Price * Quantity; // вычисляемое свойство
public Product(string name, decimal price, int quantity)
{
Name = name;
Price = price;
Quantity = quantity;
}
public override string ToString() => $"{Name}: {Price:C} × {Quantity} = {Total:C}";
}
Коллекции и LINQ
using System;
using System.Collections.Generic;
using System.Linq;
var products = new List<Product>
{
new Product("Резистор", 5.50m, 100),
new Product("Конденсатор", 12.00m, 50),
new Product("Транзистор", 45.00m, 20)
};
// Фильтрация и сортировка
var expensive = products
.Where(p => p.Price > 10)
.OrderByDescending(p => p.Total)
.ToList();
// Агрегация
decimal sum = products.Sum(p => p.Total);
decimal avg = products.Average(p => p.Price);
var maxItem = products.MaxBy(p => p.Total);
// Группировка
var groups = products
.GroupBy(p => p.Price > 10 ? "дорогие" : "дешёвые")
.Select(g => new { Категория = g.Key, Сумма = g.Sum(p => p.Total) });
foreach (var g in groups)
Console.WriteLine($"{g.Категория}: {g.Сумма:C}");
// Словарь
var byName = products.ToDictionary(p => p.Name, p => p.Quantity);
if (byName.TryGetValue("Резистор", out int qty))
Console.WriteLine($"Резисторов на складе: {qty}");
| Метод LINQ | Что делает |
|---|---|
| Where | Фильтрует по условию |
| Select | Преобразует каждый элемент |
| OrderBy / ThenBy | Сортирует по одному и следующему критерию |
| GroupBy | Группирует по ключу |
| Sum / Average / Count | Агрегирует |
| Any / All | Проверяет наличие хотя бы одного / всех |
| FirstOrDefault | Первый подходящий или значение по умолчанию |
| Join | Соединяет две коллекции по ключу |
LINQ читается как SQL и заменяет циклы с ветвлениями одной цепочкой вызовов. В отчёте это стоит подчеркнуть: код короче и его труднее сломать при правках.
Оконное приложение на Windows Forms
public partial class MainForm : Form
{
private readonly List<Product> products = new();
public MainForm()
{
InitializeComponent();
dataGridView1.AutoGenerateColumns = true;
}
private void btnAdd_Click(object sender, EventArgs e)
{
try
{
var p = new Product(
txtName.Text.Trim(),
decimal.Parse(txtPrice.Text),
int.Parse(txtQty.Text));
products.Add(p);
RefreshGrid();
ClearInputs();
}
catch (FormatException)
{
MessageBox.Show("Цена и количество должны быть числами",
"Ошибка ввода", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (ArgumentException ex)
{
MessageBox.Show(ex.Message, "Ошибка");
}
}
private void RefreshGrid()
{
dataGridView1.DataSource = null;
dataGridView1.DataSource = products;
lblTotal.Text = $"Итого: {products.Sum(p => p.Total):C}";
}
private void ClearInputs()
{
txtName.Clear(); txtPrice.Clear(); txtQty.Clear();
txtName.Focus();
}
}
Работа с базой данных
using Microsoft.Data.SqlClient;
string connStr = @"Server=(localdb)\MSSQLLocalDB;Database=Warehouse;Trusted_Connection=True;";
using var conn = new SqlConnection(connStr);
conn.Open();
// Параметризованный запрос — защита от SQL-инъекций
const string sql = "SELECT Name, Price, Quantity FROM Products WHERE Price > @minPrice";
using var cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@minPrice", 10m);
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine($"{reader.GetString(0)} — {reader.GetDecimal(1):C}");
}
Параметры вместо склейки строк — не только защита от инъекций, но и обязательный пункт в разделе безопасности курсовой. Конкатенация запроса из пользовательского ввода почти всегда получает замечание.
Полезные конструкции
// Проверка на null и значение по умолчанию
string name = input?.Trim() ?? "без названия";
// Сопоставление с образцом
string category = price switch
{
< 100 => "дешёвый",
< 1000 => "средний",
_ => "дорогой"
};
// Кортежи для возврата двух значений
(decimal min, decimal max) GetRange(List<Product> items)
=> (items.Min(p => p.Price), items.Max(p => p.Price));
// Асинхронность, чтобы интерфейс не подвисал
private async void btnLoad_Click(object sender, EventArgs e)
{
btnLoad.Enabled = false;
var data = await Task.Run(() => LoadFromDatabase());
RefreshGrid();
btnLoad.Enabled = true;
}
Структура курсовой по C#
- Постановка задачи и функциональные требования списком.
- Диаграмма классов и описание назначения каждого класса.
- Схема базы данных, если она есть, с обоснованием нормальной формы.
- Описание интерфейса со снимками экрана всех форм.
- Ключевые фрагменты кода в тексте, полный листинг — в приложение.
- Таблица тестирования: действие, ожидаемый результат, фактический.
- Руководство пользователя на одну-две страницы.
Частые вопросы
Windows Forms или WPF для курсовой?
Forms проще и быстрее: перетащили элементы, написали обработчики. WPF даёт гибкое оформление и разделение логики через MVVM, но требует освоить XAML и привязки. Если задача — успеть к сроку, берите Forms.
Чем отличается свойство от поля?
Поле хранит данные, свойство управляет доступом к ним через методы get и set. Снаружи синтаксис одинаков, поэтому поле всегда можно заменить свойством, не переписывая вызывающий код.
Зачем async, если программа работает?
Долгая операция в обработчике кнопки замораживает окно: интерфейс не отвечает, Windows помечает его как «не отвечает». async выносит работу в отдельный поток и возвращает отзывчивость.