Высокоуровневые методы информатики и программирования Лекция 8 Свойства и операции классов
План работы Свойства класса Описание операций класса Описаний преобразований объектов класса (преобразование типов) Расширяющие методы
Свойства класса Весьма полезный гибрид поля и методов: поле, доступ к которому и присваивание которому – программируемые операции Используются для: – Реализации элементов данных, которые не могут быть изменены (для этого достаточно просто не включать метод set в описание свойства) – Проверок перед присваиванием (например, проверок полномочий) – Реализации вычисляемых (активных) значений Пример: public string Name { get { return name; } set { name = value; } }
Описание свойств класса Свойства являются частными случаями методов класса и описываются следующим образом: режим_доступа { // методы для задания значения свойства set { // задание значения value некоторому закрытому полю } // методы для получения значения свойства get { return( ); } Например: public int Age { set {if (value > 0) _age = value;} get {return(_age);} } Пользователь объектов класса работает со свойствами так, как если бы они были обычными полями объектов: Person pr = new Person(); pr.Age = 25;
Пример простого класса со свойствами namespace TestProg // наше пространство имен { class NewPoint // наш класс MMM.Point { private int _x, _y; // поля класса public int x { get { return _x; } set { _x = value; } … }
Автоматически реализуемые свойства Вместо обычного определения свойства, например: private int myItem; public int MyItem { get {return myItem;} set {myItem = value;} } Можно сделать следующее описание: public int MyProperty { get; set; }
Инициализация объектов класса Значения свойствам разрешается назначать объектам во время создания. Например, если имеется описание класса, имеющего два свойства A и B: public class MyClass { public int A { get; set; } public int B { get; set; } } то можно создать и инициализировать объект данного класса следующим образом: MyClass myObject = new MyClass() { A = 5, B = 10 };
Перегрузка операций в классе Для объектов класса можно описать порядок выполнения операций путем описания статических методов, имя которых состоит из ключевого слова operator после которого стоит знак переопределяемой операции (т.е. "operator X", где X – символ перегружаемого операции). Например: public static Complex operator +(Complex c1,Complex c2) {...} В качестве параметров данного метода используются операнды, участвующие в операции. – Унарные операции имеют один параметр, – бинарные – два параметра. В каждом случае один параметр должен быть такого же типа, как класс, в котором переопределяется операция.
Возможности перегрузки операций в пользовательских типах ОперацииВозможность перегрузки +, -, !, ~, ++, -- эти унарные операции можно перегрузить +, -, *, /, %, &, |, ^, > эти бинарные операции можно перегрузить ==, !=,, = операции сравнения можно перегрузить, но только парами: если перегружена операция ==, то также должна быть перегружена операция != (и наоборот); то же самое и для операций, а также = &&, || условные логические операции нельзя перегрузить, но они вычисляются с помощью операций & и |, которые могут быть перегружены [] операция индексирования массива нельзя перегрузить, но можно определить индексаторы () операция приведения типов нельзя перегрузить, но можно определить новые операции преобразования (explicit и implicit) +=, -=, *=, /=, %=, &=, |=, ^=, >= операции присвоения нельзя перегрузить, но, например, += вычисляется с помощью операции +, допускающей перегрузку. +, -, !, ~, ++, - -, true, false эти унарные операции можно перегрузить =,., ?:, ->, new, is, sizeof, typeof эти операции перегружать нельзя!
Пример перегрузки операций
Определение преобразования типов Для класса можно описать порядок выполнения неявного или явного преобразования объектов данного класса в объекты других типов или объектов других типов в объекты данного класса. Типы преобразований: – неявное (implicit conversion) – выполняется компилятором по умолчанию; – явное (explicit conversion) – для использования нужно выполнить casting – указать в скобках требуемый тип.
Неявное преобразование типов (implicit conversion) Для описания неявного (по умолчанию) преобразования необходимо в описание класса включить: – статический метод с атрибутом implicit и – именем, состоящим из ключевого слова operator, после которого стоит название типа, в который будет выполняться преобразование. – в качестве параметра данного метода описывается переменная того типа, который будет преобразовываться. Например. Описание преобразования: // в описании Point – неявное преобразование в тип int public static implicit operator int(Point p) { int n; n = p.x; return n;} Использование неявного преобразования: Point p = new Point(5,6); int a; a = p + 2;
Явное преобразование типов (explicit conversion) Описание явного преобразования выполняется аналогично описанию неявного преобразования, но перед статическим методом нужно задавать атрибут explicit. Пример описания явного преобразования приведен ниже: // в классе Fahrenheit явное преобразование в Celsius public static explicit operator Celsius(Fahrenheit f) { return new Celsius((5.0f/9.0f)*(f.degrees- 32)); } Пример использования явного преобразования: Fahrenheit f = new Fahrenheit(100.0f); Celsius c = (Celsius)f;
Ограничения на преобразование типов Следует знать о некоторых ограничениях на описание преобразований типов: – Нельзя задать преобразование типов, если один класс является производным от другого класса (компилятор уже знает, как выполнять преобразования между наследуемыми классами). – Преобразование должно быть описано только в одном из классов (в исходном или результирующем типе). Например, на рисунке показана схема наследования классов A,B,C,D. В данном случае возможными являются только преобразования между классами C и D, так как они не связаны отношением наследования. Описание таких преобразований будет выглядеть следующим образом (если задавать явное преобразования, что является обычным для пользовательских классов): public static explicit operator D(C value) { //описание преобразования класса C в класс D } public static explicit operator C(D value) { //описание преобразования класса D в класс C } Такие преобразования можно поместить или в класс C или в класс D, но не в каждый из них.
Расширяющие методы (extension methods) После того, как класс описан, скомпилирован и занесен в сборку, то его описание становится более, или менее законченным. Можно сделать производный от него класс (если не seal), чтобы изменить его поведение. В C# можно расширять откомпилированные классы с помощью extension методов. Расширяющие методы позволяют добавить к существующим классам, для которых нет исходного кода, новые методы. Расширяющие методы нужно описывать в статических классах. Первый параметр (и только он) расширяющего метода должен иметь модификатор this (после него нельзя ref).
Расширяющие методы Расширяющий метод (extension method) это статический метод статического класса, который можно вызывать как будто он метод экземпляра другого класса Например: можно создать расширяющий метод с именем ToDouble, который является статическим методом в статическом классе названный StringConversions, но который будет вызываться для объектов типа string. Напоминание: Есть два типа методов: – Статические методы – вызываются с только после имени класса. Array.Sort(ar); – Методы экземпляров класса – вызываются только после ссылки на объект класса. ar.Length();
Объявление расширяющих методов (РМ) Если первый параметр метода описан с модификатором (ключевым словом) this, то этот метод будет расширяющим. Расширяющий метод будет казаться методом экземпляра любого объекта с таким же типом, как и первый параметр расширяющего метода. Например, если первый параметр РМ имеет тип string, то расширяющий метод будет казаться методом экземпляра класса string и может быть вызван для любого объекта типа string. Расширяющие методы могут описываться только в статических классах. При вызове РМ первый параметр не передается!
Пример расширяющего метода static class MyExtensions { // данный метод позволяет у любой целой переменной поменять цифры в обратном порядке // например, k = 56, то будет возвращаться, число 65. public static int ReverseDigits(this int i) { // преобразуем в строку и преобразуем все символы в массив char[] digits = i.ToString().ToCharArray(); // теперь переставляем элементы массива в обратном порядке Array.Reverse(digits); // преобразуем обратно массив символов в строку string newDigits = new string(digits); // и наконец возвращаем измененную строку Finally, return the modified string back as an int. int rc = int.Parse(newDigits); return rc; } Пример использования: int k = 58; int kr = k.ReverseDigits(); // kr = 85
Пример расширяющих методов Расширяющие методы для типа int: static class Extensions { // удвоение значения public static int Mult2(this int i) { i *= 2; return i; } // утроение значения public static int Mult3(this int i) { i *= 3; return i; } } … int n =5; int m = n.Mult3(); // m = 15
XML-комментарии Встроенная функциональность для документирования кода Комментарии с " /// " экспортируются Компиляция с опцией /doc -> генерируется XML-документация Имеет предопрелеленную схему Сравните: В Java приходится использовать утилиту командной строки javadoc для генерации HTML- (а не XML-) документации на основе документирующих комментариев Пример: /// /// This function serves to calculate the /// overall value of the item including all /// taxes /// /// /// Tax calculates in CalcTax() /// public decimal GetTotalValue() { … }