Даб работа по методам программирования. обновлено пронько. 1. Классы в C#. Модификаторы доступа. Поля, свойства, индексаторы
Скачать 0.78 Mb.
|
5. Интерфейсы. Использование интерфейсов. Применение стандартных интерфейсов .NET Interface в C# — это языковая конструкция, похожая с точки зрения синтаксиса на class, но фундаментально отличающаяся от него. Интерфейс можно сравнить с абстрактным классом, у которого только абстрактный метод, то есть нет никакой его реализации. У него есть функция множественного наследования (когда один класс наследуется сразу от нескольких). Поскольку в интерфейсах не заложена реализация, в них указывается только то, что необходимо сделать, но нет данных о том, как это сделать. Определяются свойства, функционал и методы без их реализации. public interface ITaxCalculator { int Calculate(); } Обратите внимание на то, что для объектов интерфейсов в C# чаще всего используется вначале буква I, но это требование не обязательно. Что определяет interface in C#? События. Свойства (interface property в C#). Методы. Константы. Индексаторы. Важно: интерфейс не может содержать поля свойства, реализуемые автоматически. Модификаторы доступа интерфейсов Члены классов имеют свои модификаторы доступа. Они определяют контекст применения метода или использования переменной. Уровень доступа интерфейсов по умолчанию internal (только в рамках проекта). Чтобы сделать его общедоступным, применяется модификатор public — доступен из любого места в коде и даже из других программ: public interface IMovable { void Move(); } Полная противоположность public — модификатор private. Это закрытый класс, который может быть доступен из кода в одном и том же классе/контексте. Предоставляет доступ с минимальными правами. Interface in C# и его применение Объекты интерфейсов нельзя создать с помощью конструктора. Интерфейсы C# реализовываются в классах и структурах. Например: using System; namespace HelloApp { interface IMovable { void Move(); } class Person : IMovable { public void Move() { Console.WriteLine("Буря мглою"); } } struct Car : IMovable { public void Move() { Console.WriteLine("Небо кроет"); } } class Program { static void Action(IMovable movable) { movable.Move(); } static void Main(string[] args) { Person person = new Person(); Car car = new Car(); Action(person); Action(car); Console.Read(); } } } В программе есть метод Action(), а принятый им объект интерфейса — IMovable. При написании кода ничего об объекте неизвестно, но мы знаем, что метод Move будет им обязательно реализован. Поэтому мы можем вызвать этот метод. Внимание! Если в свойствах и методах интерфейса отсутствуют модификаторы доступа, по умолчанию они являются public. Вывод программы в консоли: Буря мглою Небокроет What is interface in С#? По сути, интерфейс — это залог того, что определенный тип обязательно реализует какой-то конкретный функционал. Interface implementation: как реализуются интерфейсы в C# по умолчанию Что мы знали раньше об интерфейсах? Они содержат только методы, свойства, события, индексаторы. Они не содержат полей. По умолчанию они public и abstract. Не включают защищенные, внутренние и частные члены. Но начиная с версии C# 8.0 методы и свойства могут реализовываться в интерфейсах по умолчанию. Допустим, что интерфейс в приложении реализуется классами. Чтобы добавить новый член, вам нужно обновить все классы для его поддержки или создать расширенный интерфейс с новым методом, который будет унаследован от предыдущего: interface ICar { void GetSpeed(); void GetMileage(); } interface IInternetCar : ICar { void SendCommand(); } Но чем больше методов и функций в расширенном интерфейсе, тем все быстрее он становится неуправляемым. Эту проблему решают интерфейсы, реализуемые по умолчанию. Они полезны при добавлении новых членов, не нарушая существующую реализацию: interface ICar { void GetSpeed(); void GetMileage(); public void SendCommand() { //Send the Command. } } С interface implementation в C# нет никакой необходимости в применении новых интерфейсов и изменениях в существующей реализации. Стандартные интерфейсы В библиотеке .NET определено множество стандартных интерфейсов, задающих желаемое поведение объектов. Например, интерфейс IComparable задает метод сравнения объектов по принципу больше или меньше, что позволяет выполнять их сортировку. Реализация интерфейсов IEnumerable и IEnumerator дает возможность просматривать содержимое объекта с помощью конструкции foreach, а реализация интерфейса ICloneable – клонировать объекты. Стандартные интерфейсы поддерживаются многими стандартными классами библиотеки. Например, работа с массивами с помощью цикла foreach возможна именно потому, что тип Array реализует интерфейсы IEnumerable и IEnumerator. Можно создавать и собственные классы, поддерживающие стандартные интерфейсы, что позволит использовать. interface IComparable { int CompareTo(object obj) } 6. Делегаты. Делегаты — это указатели на методы и с помощью делегатов мы можем вызвать данные методы. Методы могут как присваиваться напрямую, так и передаваться в конструктор. Делегаты могут объявляться как в классе, так и за его пределами. Для объявления делегата используется ключевое слово delegate, после которого идет возвращаемый тип, название и параметры (модификаторы ref, in и out тоже играют роль). Например: 1. Operation operation = Add; // делегат указывает на метод Add 2. int result = operation(4, 5); // фактически Add(4, 5) 3. Console.WriteLine(result); // 9 4. 5. operation = Multiply; // теперь делегат указывает на метод Multiply 6. result = operation(4, 5); // фактически Multiply(4, 5) 7. Console.WriteLine(result); // 20 8. 9. int Add(int x, int y) => x + y; 10. int Multiply(int x, int y) => x * y; 11. delegate int Operation(int x, int y); В данном случае делегат Operation возвращает значение типа int и имеет два параметра типа int. Поэтому этому делегату соответствует любой метод, который возвращает значение типа int и принимает два параметра типа int. В данном случае это методы Add и Multiply. То есть мы можем присвоить переменной делегата любой из этих методов и вызывать. Делегат может указывать на множество методов, которые имеют ту же сигнатуру и возвращаемые тип. Все методы в делегате попадают в специальный список — список вызова или invocation list. И при вызове делегата все методы из этого списка последовательно вызываются. Для добавления/удаления методов из делегата применяется операция или Delegate.Combine/Delegate.Remove: 1. Operation operation = Add; 2. operation += Multiply; 3. operation -= Add; Однако стоит отметить, что в реальности будет происходить создание нового объекта делегата, и новый созданный объект делегата будет присвоен исходной переменной. Также стоит отметить, что делегат может содержать несколько одинаковых методов или не содержать ни одного и иметь значение null. При удалении следует учитывать, что если делегат содержит несколько ссылок на один и тот же метод, то операция начинает поиск с конца списка вызова делегата и удаляет только первое найденное вхождение. Если подобного метода в списке вызова делегата нет, то операция не имеет никакого эффекта. Делегаты можно объединять. Многоадресный делегат содержит список присвоенных ему делегатов. При вызове многоадресного делегата поочередно вызываются представленные в его списке делегаты. Объединять можно только делегаты одного типа. Для этого используют оператор +. С помощью оператора можно удалить делегат, входящий в состав многоадресного делегата. Если делегат возвращает некоторое значение, то возвращается значение последнего метода из списка вызова (если в списке вызова несколько методов). Например: 1. Operation operation = Multiply; 2. operation += Add; 3. Console.WriteLine(operation(7, 2)); // Add(7,2) = 9 4. Console.WriteLine(operation.Invoke(7, 2)); // Add(7,2) = 9 Делегаты могут быть обобщенными. Например: 1. delegate T Operation Делегаты могут быть параметрами методов. Например: 1. void DoOperation(int a, int b, Operation op) 2. { 3. Console.WriteLine(op(a,b)); 4. } Делегаты могут возвращаться из методов. Например: 1. Operation SelectOperation(OperationType opType) 2. { 3. switch (opType) 4. { 5. case OperationType.Add: return Add; 6. case OperationType.Subtract: return Subtract; 7. default: return Multiply; 8. } 9. } В данном случае метод SelectOperation() в качестве параметра принимает перечисление типа OperationType. Это перечисление хранит три константы, каждая из которых соответствует определенной арифметической операции. И в самом методе в зависимости от значения параметра возвращаем определенный метод. 7. События. События сигнализируют системе о том, что произошло определенное действие. И если нам надо отследить эти действия, то как раз мы можем применять события. События объявляются в классе с помощью ключевого слова event, после которого указывается тип делегата, который представляет событие:
В данном случае вначале определяется делегат AccountHandler, который принимает один параметр типа string. Затем с помощью ключевого слова event определяется событие с именем Notify, которое представляет делегат AccountHandler. Название для события может быть произвольным, но в любом случае оно должно представлять некоторый делегат. Определив событие, мы можем его вызвать в программе как метод, используя имя события:
Однако при вызове событий мы можем столкнуться с тем, что событие равно null в случае, если для его не определен обработчик. Поэтому при вызове события лучше его всегда проверять на null.
С событием может быть связан один или несколько обработчиков. Обработчики событий — это метод, который выполняется при вызове событий. Каждый обработчик событий по списку параметров и возвращаемому типу должен соответствовать делегату, который представляет событие. Для добавления обработчика события применяется операция +=, удаление же происходит при помощи операции -=.
В качестве обработчиков могут использоваться не только обычные методы, но также делегаты, анонимные методы и лямбда-выражения. C помощью специальных акссесоров add/remove мы можем управлять добавлением и удалением обработчиков. Как правило, подобная функциональность редко требуется, но тем не менее мы ее можем использовать. Например:
Аксессор add вызывается при добавлении обработчика, то есть при операции +=. Добавляемый обработчик доступен через ключевое слово value. Блок remove вызывается при удалении обработчика. Нередко при возникновении события обработчику события требуется передать некоторую информацию о событии. Например, добавим и в нашу программу новый класс AccountEventArgs со следующим кодом:
Теперь применим класс AccoutEventArgs, изменив класс Account следующим образом:
По сравнению с предыдущей версией класса Account здесь изменилось только количество параметров у делегата и соответственно количество параметров при вызове события. Теперь делегат AccountHandler в качестве первого параметра принимает объект, который вызвал событие, то есть текущий объект Account. А в качестве второго параметра принимает объект AccountEventArgs, который хранит информацию о событии, получаемую через конструктор. 8. Строки (массивы символов, String, StringBuilder). Кортежи. Символы и массивы символов отдельные символы, чаще всего, его называют типом char; строки постоянной длины, часто они представляются массивом символов; строки переменной длины – это, как правило, тип string, соответствующий современному представлению о строковом типе. String и string String — это последовательная коллекция сhar объектов, представляющих строку; Char объект соответствует единице кода UTF-16. Значение String объекта неизменяемо (то есть доступно только для чтения); string является псевдонимом в C# для System.String. Когда мы выполняем какой-нибудь метод класса String, система создает новый объект в памяти с выделением ему достаточного места. Удаление первого символа - не самая затратная операция. Однако когда подобных операций множество, а объем текста, для которого надо выполнить данные операции, также не самый маленький, то издержки при потере производительности становятся более существенными. StringBuilder Чтобы выйти из ситуации c неизменяемой строкой во фреймворк .NET был добавлен новый класс StringBuilder, который находится в пространстве имен System.Text. Этот класс представляет динамическую строку. Для хранения длины строки в классе StringBuilder определенно свойство Length. Однако есть и вторая величина - емкость выделенной памяти. Это значение хранится в свойстве Capacity. Емкость - это выделенная память под объект. Установка емкости позволяет уменьшить выделения памяти и тем самым повысить производительность. Например, посмотрим, что содержат данные свойства: 1. using System.Text; 2. 3. StringBuilder sb = new StringBuilder("Привет мир"); 4. Console.WriteLine($"Длина: {sb.Length}"); // Длина: 10 5. Console.WriteLine($"Емкость: {sb.Capacity}"); // Емкость: 16 |