#set text(size: 1.3em)

#show raw.where(block: false): box.with(
  fill: luma(240),
  inset: (x: 3pt, y: 0pt),
  outset: (y: 3pt),
  radius: 2pt,
)

#show raw.where(block: true): block.with(
  fill: luma(240),
  inset: 10pt,
  radius: 4pt,
)
    
// title
    
#align(center)[Санкт-Петербургский национальный исследовательский университет информационных технологий, механики и оптики]
\
\
\
#align(center)[Факультет инфокоммуникационных технологий]
#align(center)[Направление подготовки 11.03.02]
\
\
#align(center)[Лабораторная работа №9]
#align(center)[Применение делегатов и событий]
\
\
\ //#align(center)[Вариант 19]
\
\
\
\
\
\
\
#align(right)[Выполнил:]
#align(right)[Дощенников Никита Андреевич]
#align(right)[Группа: К3221]
#align(right)[Проверил:]
#align(right)[Иванов Сергей Евгеньевич]
\
\
#align(center)[Санкт-Петербург]
#align(center)[2025]

#pagebreak()
    
=== Цель работы: 

Научиться работать с делегатами и событиями и применять их.

=== Упражнение 1. Использование делегата при вызове метода.

В этом упражнении я реализовал в классе `Book` возможность вызова метода через делегат.

```cs
using System;

class Operation
{
    public static void PrintTitle(Book b)
    {
        b.Show();
    }
}
```

```cs
using System;

class Book : Item
{
    private String author;
    private String title;
    private String publisher;
    private int pages;
    private int year;

    private static double price = 9;

    static Book()
    {
        price = 10;
    }

    public Book()
    {

    }


    public Book(String author, String title, String publisher, int pages, int year, long invNumber, bool taken) : base (invNumber, taken)
    {
        this.author = author;
        this.title = title;
        this.publisher = publisher;
        this.pages = pages;
        this.year = year;
    }

    public Book(String author, String title)
    {
        this.author = author;
        this.title = title;
    }

    public void SetBook(String author, String title, String publisher, int pages, int year)
    {
        this.author = author;
        this.title = title;
        this.publisher = publisher;
        this.pages = pages;
        this.year = year;
    }

    public static void SetPrice(double price)
    {
        Book.price = price;
    }


    public override void Show()
    {
        Console.WriteLine("\nКнига:\n Автор: {0}\n Название: {1}\n Год издания: {2}\n {3} стр.\n Стоимость аренды: {4}", author, title, year, pages, Book.price);
        base.Show();
    }


    public double PriceBook(int s)
    {
        double cost = s * price;
        return cost;
    }

    public override void Return()
    {
        taken = ReturnSrok;
    }

    public delegate void ProcessBookDelegate(Book book);

    public bool ReturnSrok { get; set; }

    public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
    {
        if (ReturnSrok)
        processBook(this);
    }
}
```

Тесты: 

```cs
public static void Main(string[] args)
{
    Book b4 = new Book("Толстой Л.Н.", "Анна Каренина", "Знание", 1204, 2014, 103, true);
    Book b5 = new Book("Неш Т", "Программирование для профессионалов", "Вильямс", 1200, 2014, 108, true);

    b4.ReturnSrok = true;
    b5.ReturnSrok = false;

    Console.WriteLine("\nКниги возвращены в срок:");
    b4.ProcessPaperbackBooks(Operation.PrintTitle); 
    b5.ProcessPaperbackBooks(Operation.PrintTitle);
}
```

Пример: 

#align(center)[#image("assets/1.png")]

=== Упражнение 2. Работа с событиями.

В этом упражнении я в классе `Book` объявил событие "возвращение книги в срок", и объекты этого класса смогли уведомлять объекты других классов о данном событии.

```cs
using System;

class Book : Item
{
    private String author;
    private String title;
    private String publisher;
    private int pages;
    private int year;

    private static double price = 9;
    private bool returnSrok = false;

    static Book()
    {
        price = 10;
    }

    public Book()
    {

    }


    public Book(String author, String title, String publisher, int pages, int year, long invNumber, bool taken) : base (invNumber, taken)
    {
        this.author = author;
        this.title = title;
        this.publisher = publisher;
        this.pages = pages;
        this.year = year;
    }

    public Book(String author, String title)
    {
        this.author = author;
        this.title = title;
    }

    public void SetBook(String author, String title, String publisher, int pages, int year)
    {
        this.author = author;
        this.title = title;
        this.publisher = publisher;
        this.pages = pages;
        this.year = year;
    }

    public static void SetPrice(double price)
    {
        Book.price = price;
    }


    public override void Show()
    {
        Console.WriteLine("\nКнига:\n Автор: {0}\n Название: {1}\n Год издания: {2}\n {3} стр.\n Стоимость аренды: {4}", author, title, year, pages, Book.price);
        base.Show();
    }


    public double PriceBook(int s)
    {
        double cost = s * price;
        return cost;
    }

    public override void Return()
    {
        taken = ReturnSrok;
    }

    public delegate void ProcessBookDelegate(Book book);

    public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
    {
        if (ReturnSrok)
        processBook(this);
   }

    public static event ProcessBookDelegate RetSrok;

    public bool ReturnSrok
    {
        get
        {
            return returnSrok;
        }
        set 
        {
            returnSrok = value;
            if (ReturnSrok == true)
                RetSrok(this);
        }
    }

    public override string ToString()
    {
        string str = this.title + ", " + this.author + " Инв. номер " + this.invNumber;
        return str;
    }
}
```

```cs
using System;

class Operation
{
    public static void PrintTitle(Book b)
    {
        b.Show();
    }

    public static void MetodObrabotchik(Book b)
    {
        Console.WriteLine("Книга {0} сдана в срок.", b.ToString());
    }
}
```

Тесты: 

```cs
public static void Main(string[] args)
{
    Book b4 = new Book("Толстой Л.Н.", "Анна Каренина", "Знание", 1204, 2014, 103, true);
    Book b5 = new Book("Неш Т", "Программирование для профессионалов", "Вильямс", 1200, 2014, 108, true);

    Book.RetSrok += new Book.ProcessBookDelegate(Operation.MetodObrabotchik);

    b4.ReturnSrok = true;
    b5.ReturnSrok = true;

    Console.WriteLine("\nКниги возвращены в срок:");
    b4.ProcessPaperbackBooks(Operation.PrintTitle); 
    b5.ProcessPaperbackBooks(Operation.PrintTitle);
}
```

Пример: 

#align(center)[#image("assets/2.png")]

=== Упражнение 3. Реализация события.

В этом упражнении в проекте `IgralnayaKost` я реализовал возникновение события "выпало максимальное количество очков" при броске игрального кубика.

```cs
using System;

class Gamer
{
    string Name;
    IgralnayaKost seans;

    public Gamer(string name)
    {
        Name = name;
        seans = new IgralnayaKost();
        seans.MaxRolled += OnMaxRolled;
    }

    void OnMaxRolled(int value)
    {
        Console.WriteLine("Событие: у игрока {0} выпало максимальное количество очков ({1})", Name, value);
    }

    public int SeansGame()
    {
        return seans.random();
    }

    public override string ToString()
    {
        return Name;
    }
}
```

```cs
using System;

class IgralnayaKost 
{
    readonly Random r;
    public event Action<int> MaxRolled;

    public IgralnayaKost()
    {
        r = new Random();
    }

    public int random()
    {
        int res = r.Next(6) + 1;
        if (res == 6) MaxRolled?.Invoke(res);
        return res;
    }
}
```

Тесты: 

```cs
public static void Main(string[] args)
{
    Gamer g1 = new Gamer("Niko");

    for (int i = 1; i <= 6; i++)
    {
        Console.WriteLine("Выпало количество очков {0} для игрока {1}", g1.SeansGame(), g1.ToString());
    }
}
```

Пример: 

#align(center)[#image("assets/3.png")]

=== Упражнение 4. Иерархия классов учебного центра.

В этом задании я реализовал иерархию классов учебного центра.

```cs
using System;

class Administrator : Person, IEmployee
{
    public string Laboratory { get; }
    public string Department => Laboratory;
    public string Position => "Администратор";
    public int Experience { get; }
    public decimal BaseRate { get; }

    public Administrator(string lastName, DateTime birthDate, string laboratory, int experience, decimal baseRate)
        : base(lastName, birthDate)
    {
        Laboratory = laboratory;
        Experience = experience;
        BaseRate = baseRate;
    }

    public override void Show()
    {
        Console.WriteLine("Администратор: {0}, лаб.: {1}, стаж: {2} лет, возраст: {3}", LastName, Laboratory, Experience, Age());
    }

    public decimal GetMonthlyPay()
    {
        return BaseRate * (1 + 0.05m * Experience);
    }
}
```

```cs
using System;

interface IEmployee
{
    string Department { get; }
    string Position { get; }
    int Experience { get; }
    decimal GetMonthlyPay();
}
```

```cs
using System;

class Manager : Person, IEmployee
{
    public string Faculty { get; }
    public string Role { get; }
    public int Experience { get; }
    public decimal BaseRate { get; }
    public string Department => Faculty;
    public string Position => Role;

    public Manager(string lastName, DateTime birthDate, string faculty, string role, int experience, decimal baseRate)
        : base(lastName, birthDate)
    {
        Faculty = faculty;
        Role = role;
        Experience = experience;
        BaseRate = baseRate;
    }

    public override void Show()
    {
        Console.WriteLine("Менеджер: {0}, факультет: {1}, должность: {2}, стаж: {3} лет, возраст: {4}", LastName, Faculty, Role, Experience, Age());
    }

    public decimal GetMonthlyPay()
    {
        return BaseRate * (1 + 0.06m * Experience);
    }
}
```

```cs
using System;

abstract class Person
{
    public string LastName { get; }
    public DateTime BirthDate { get; }

    protected Person(string lastName, DateTime birthDate)
    {
        LastName = lastName;
        BirthDate = birthDate;
    }

    public int Age()
    {
        var today = DateTime.Today;
        int a = today.Year - BirthDate.Year;
        if (BirthDate.Date > today.AddYears(-a)) a--;
        return a;
    }

    public virtual void Show()
    {
        Console.WriteLine("{0}, возраст: {1}", LastName, Age());
    }
}
```

```cs
using System;

class Student : Person
{
    public string Faculty { get; }
    public int Course { get; }

    public Student(string lastName, DateTime birthDate, string faculty, int course)
        : base(lastName, birthDate)
    {
        Faculty = faculty;
        Course = course;
    }

    public override void Show()
    {
        Console.WriteLine("Студент: {0}, факультет: {1}, курс: {2}, возраст: {3}", LastName, Faculty, Course, Age());
    }
}
```

```cs
using System;

class Teacher : Person, IEmployee
{
    public string Faculty { get; }
    public string Rank { get; }
    public int Experience { get; }
    public decimal BaseRate { get; }
    public string Department => Faculty;
    public string Position => Rank;

    public Teacher(string lastName, DateTime birthDate, string faculty, string rank, int experience, decimal baseRate)
        : base(lastName, birthDate)
    {
        Faculty = faculty;
        Rank = rank;
        Experience = experience;
        BaseRate = baseRate;
    }

    public override void Show()
    {
        Console.WriteLine("Преподаватель: {0}, факультет: {1}, должность: {2}, стаж: {3} лет, возраст: {4}", LastName, Faculty, Rank, Experience, Age());
    }

    public decimal GetMonthlyPay()
    {
        decimal mult = Rank.ToLower().Contains("проф") ? 1.4m : Rank.ToLower().Contains("доцент") ? 1.2m : 1.0m;
        return BaseRate * mult * (1 + 0.04m * Experience);
    }
}
```

Тесты: 

```cs
using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main(string[] args)
    {
        var people = new List<Person>
        {
            new Administrator("Иванова", new DateTime(1985, 3, 12), "Лаборатория сетей", 8, 900m),
            new Student("Петров", new DateTime(2005, 11, 2), "ФКТИ", 2),
            new Teacher("Сидоров", new DateTime(1979, 6, 25), "ФКТИ", "Доцент", 12, 1400m),
            new Manager("Кузнецова", new DateTime(1990, 1, 5), "ФКТИ", "Менеджер проектов", 6, 1100m),
            new Student("Орлова", new DateTime(2003, 4, 17), "ФПМИ", 4)
        };

        foreach (var p in people) p.Show();

        Console.WriteLine();
        int from = 20, to = 40;
        var inRange = people.Where(p => p.Age() >= from && p.Age() <= to);
        foreach (var p in inRange) p.Show();

        Console.WriteLine();
        foreach (var e in people.OfType<IEmployee>())
            Console.WriteLine("{0}: {1}, подразделение: {2}, оклад: {3:F2}", e.GetType().Name, e.Position, e.Department, e.GetMonthlyPay());
    }
}
```

Пример: 

#align(center)[#image("assets/4.png")]

=== Вывод.

В ходе проделанной работы я научился работать с делегатами и событиями и применять их.

=== Code review. (by #link("https://zzzcode.ai")[zzzcode.ai])

*Резюме*

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

*Ошибка*

В методе `random()` класса `IgralnayaKost` название метода не соответствует стандартам именования в C\#. Рекомендуется переименовать его в `Roll()` для лучшего понимания.

*Стиль кода*

Код в целом следует стандартам C\#, однако есть некоторые моменты, которые можно улучшить: Имена переменных и методов должны быть написаны в стиле `PascalCase`. Необходимо использовать явные модификаторы доступа (например, `public`, `private`) для всех членов класса.

*Структура кода*

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

*Читаемость*

Читаемость кода можно улучшить, добавив комментарии к методам и классам, а также следуя единообразному стилю именования. Например, использование `seans` как имени переменной может быть неочевидным для других разработчиков.

*Производительность*

Код работает эффективно для текущих задач, однако стоит обратить внимание на создание экземпляра `Random` в каждом вызове. Рекомендуется создать один экземпляр `Random` и использовать его повторно.

*Масштабируемость*

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

*Безопасность*

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

*Обработка ошибок*

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

*Заключение*

Код представляет собой хорошую основу для игры, но требует некоторых улучшений в области стиля, читаемости и обработки ошибок. Рекомендуется внести предложенные изменения для повышения качества и удобства работы с кодом.


