Visual studio переопределить метод
При рассмотрении основ объектно-ориентированного программирования упоминалось переопределение методов. Итак, давайте разберемся, что это такое, и с чем это употребляют. По сути, это крайне удобный механизм. При помощи переопределения метода мы можем расширить его функциональность в дочерних классах. А при необходимости метод родительского класса может быть полностью переписан.
Чтобы сказать системе, что нам нужна возможность переопределить метод, нужно использовать слово virtual. К примеру, при описании класса Object метод ToString() имеет подобную запись.
Отметив метод словом virtual мы оповещаем систему, что метод может быть переопределен в любом наследнике. А учитывая возможность построения древовидной структуры наследования — возможность переопределения будет очень полезной фичей. Вернемся к примеру класса «Персонаж». Переопределим для него метод ToString().
Рассмотрим пример использования переопределенного метода.
Вызвав приведение к строке у экземпляра класса, мы стопроцентно получим результат работы переопределенного метода. А что будет во втором случае? Тут все напрямую зависит от того, как мы переопределили метод.
override
Если было использовано это ключевое слово, на выходе будет использоваться переопределенный метод. Вне зависимости от того, как было оформлено обращение к методу.
Использование этого ключевого слова говорит системе о том, что мы не хотим перекрыть реализацию метода в базовом классе. То есть, при необходимости сможем получить доступ к методу предка. Рассмотрим переопределение метода при помощи слова new. В качестве примера перегрузим еще один метод класса Object.
Как вы видите, в методе мы проводим простое сравнение свойств переданного объекта, с объектом вызвавшим метод. Вместо override мы использовали new потому, как знаем, что не нужно перекрывать функциональность метода родителя.
Итак, в чем же фишка? Когда мы вызовем сравнение двух персонажей, будет вызван переопределенный метод. Следовательно сравнение будет проведено по свойствам. Если же сравнение вызвать из obj, на выходе будет результат сравнения двух объектов на полную идентичность. Если бы мы использовали ключевое слово override, вызвать родительский метод уже не получилось бы.
Пожалуй, это необходимый минимум, которым следует владеть для переопределения методов. Удачных вам экспериментов, коллеги.
Что? Эта возможность позволяет создавать методы Equals и GetHashCode.
Когда? Создавайте эти переопределения, если у вас есть тип, который нужно сравнить по одному или нескольким полям, а не по расположению объектов в памяти.
Зачем?
При реализации типа значения рекомендуется переопределить метод Equals. При этом обеспечивается повышенная производительность по сравнению с реализацией метода Equals по умолчанию для ValueType.
При реализации ссылочного типа рекомендуется переопределить метод Equals, если ваш тип выглядит как базовый, например Point, String, BigNumber и т. д.
Переопределите метод GetHashCode, чтобы тип правильно работал в хэш-таблице. Дополнительные сведения см. в руководстве по операторам равенства.
Практические советы
Поместите курсор в любую позицию на строке объявления типа.
Теперь код должен выглядеть как на следующем снимке экрана:
Не выбирайте имя типа двойным щелчком, иначе параметр меню будет недоступен. Просто установите курсор в любую позицию на строке.
Далее выберите одно из следующих действий:
Нажмите клавиши CTRL+ . чтобы открыть меню Быстрые действия и рефакторинг.
Щелкните правой кнопкой мыши и выберите меню Быстрые действия и рефакторинг.
В раскрывающемся меню выберите Создать Equals(object) или Создать Equals и GetHashCode.
В диалоговом окне Выбрать члены выберите члены, для которых хотите создать методы:
В этом диалоговом окне также можно создавать операторы, используя флажок в нижней части окна.
Методы Equals и GetHashCode создаются с реализациями по умолчанию, как показано в следующем коде:
Определение DSL — это платформа, на которой можно создать мощный набор средств, основанных на предметно-ориентированном языке. Многие расширения и адаптации могут быть сделаны путем переопределения и расширения классов, созданных из определения DSL. Эти классы включают не только доменные классы, явно определенные на схеме определения DSL, но также и другие классы, определяющие область элементов, обозреватель, сериализацию и т. д.
Механизмы расширяемости
Предусмотрено несколько механизмов, позволяющих расширить созданный код.
Переопределение методов в разделяемом классе
Определения разделяемых классов позволяют определять класс более чем в одном месте. Это позволяет отделить созданный код от кода, который вы пишете самостоятельно. В коде, написанном вручную, можно переопределить классы, наследуемые созданным кодом.
Например, если в определении DSL вы определили доменный класс Book , можно написать пользовательский код, который добавляет методы переопределения:
Чтобы переопределить методы в созданном классе, всегда запишите код в файл, отделенный от созданных файлов. Как правило, файл содержится в папке с именем значение CustomCode. При внесении изменений в созданный код они будут потеряны при повторном создании кода из определения DSL.
Чтобы узнать, какие методы можно переопределить, введите override в классе, за которым следует пробел. Подсказка IntelliSense сообщит, какие методы можно переопределить.
Классы Double-Derived
Большинство методов в созданных классах наследуются от фиксированного набора классов в пространствах имен моделирования. Однако некоторые методы определяются в созданном коде. Обычно это означает, что их нельзя переопределить; нельзя переопределить в одном разделяемом классе методы, определенные в другом частичном определении того же класса.
Тем не менее, эти методы можно переопределить, установив для доменного класса флаг Generated Double производный . Это приводит к созданию двух классов, один из которых является абстрактным базовым классом другого. Все определения методов и свойств находятся в базовом классе, и только конструктор находится в производном классе.
Например, в образце библиотеки. DSL CirculationBook класс домена имеет свойство, для которого Generates``Double Derived задано значение true . Созданный код для этого доменного класса содержит два класса:
CirculationBookBase , который является абстрактным и содержит все методы и свойства.
CirculationBook , который является производным от CirculationBookBase . Он пуст, за исключением конструкторов.
Чтобы переопределить любой метод, создайте частичное определение производного класса, например CirculationBook . Можно переопределить как созданные методы, так и методы, унаследованные от платформы моделирования.
Этот метод можно использовать со всеми типами элементов, включая элементы модели, связи, фигуры, диаграммы и соединители. Можно также переопределить методы других созданных классов. Некоторые созданные классы, такие как Тулбоксхелпер, всегда являются двойным производным.
Пользовательские конструкторы
Нельзя переопределить конструктор. Даже в классах, производных от Double, конструктор должен находиться в производном классе.
Если вы хотите предоставить собственный конструктор, это можно сделать, задав Has Custom Constructor для доменного класса в определении DSL. При нажатии кнопки преобразовать все шаблоны созданный код не будет включать конструктор для этого класса. Он будет содержать вызов отсутствующего конструктора. Это приводит к созданию отчета об ошибке при построении решения. Дважды щелкните отчет об ошибках, чтобы просмотреть комментарий в созданном коде, поясняющий, что следует предоставить.
Напишите определение разделяемого класса в файле, отдельном от создаваемых файлов, и предоставьте конструктор.
Точки расширения с флагами
Помеченная точка расширения — это место в определении DSL, в котором можно установить свойство или флажок, чтобы указать, что будет предоставлен пользовательский метод. Одним из примеров являются пользовательские конструкторы. другие примеры включают задание значения для Kind свойства домена вычисляемым или настраиваемым служба хранилища или задание пользовательского флага в построителе подключений.
В каждом случае при установке флага и повторном создании кода будет выдаваться ошибка сборки. Дважды щелкните ошибку, чтобы просмотреть комментарий, который необходимо предоставить.
Правила
Диспетчер транзакций позволяет определять правила, выполняемые до окончания транзакции, в которой произошло указанное событие, например изменение свойства. Правила обычно используются для поддержания синчронисм между различными элементами в хранилище. Например, правила используются, чтобы убедиться, что на диаграмме отображается текущее состояние модели.
Правила определяются отдельно для каждого класса, поэтому нет необходимости иметь код, регистрирующий правило для каждого объекта. Дополнительные сведения см. в разделе правила распространяют изменения в модели.
Сохранить события
Хранилище моделей предоставляет механизм событий, который можно использовать для прослушивания конкретных типов изменений в хранилище, включая добавление и удаление элементов, изменения значений свойств и т. д. Обработчики событий вызываются после закрытия транзакции, в которой были внесены изменения. Как правило, эти события используются для обновления ресурсов за пределами хранилища.
Вы можете подписываться на некоторые события в фигурах. Например, можно ожидать щелчков мышью на фигуре. Необходимо написать код, который подписывается на событие для каждого объекта. Этот код можно написать с помощью переопределения Инитиализеинстанцересаурцес ().
Некоторые события создаются в ShapeFields, которые используются для рисования декораторов на фигуре. Пример см. в разделе как перехватить щелчок фигуры или декоратора.
Эти события обычно не возникают внутри транзакции. Если необходимо внести изменения в хранилище, следует создать транзакцию.
При наследовании нередко возникает необходимость изменить в классе-наследнике функционал метода, который был унаследован от базового класса. В этом случае класс-наследник может переопределять методы и свойства базового класса.
Те методы и свойства, которые мы хотим сделать доступными для переопределения, в базовом классе помечается модификатором virtual . Такие методы и свойства называют виртуальными.
А чтобы переопределить метод в классе-наследнике, этот метод определяется с модификатором override . Переопределенный метод в классе-наследнике должен иметь тот же набор параметров, что и виртуальный метод в базовом классе.
Например, рассмотрим следующие классы:
Здесь класс Person представляет человека. Класс Employee наследуется от Person и представляет сотруднника предприятия. Этот класс кроме унаследованного свойства Name имеет еще одно свойство - Company.
Чтобы сделать метод Print доступным для переопределения, этот метод определен с модификатором virtual . Поэтому мы можем переопределить этот метод, но можем и не переопределять. Допустим, нас устраивает реализация метода из базового класса. В этом случае объекты Employee будут использовать реализацию метода Print из класса Person:
Но также можем переопределить виртуальный метод. Для этого в классе-наследнике определяется метод с модификатором override , который имеет то же самое имя и набор параметров:
Возьмем те же самые объекты:
Виртуальные методы базового класса определяют интерфейс всей иерархии, то есть в любом производном классе, который не является прямым наследником от базового класса, можно переопределить виртуальные методы. Например, мы можем определить класс Manager, который будет производным от Employee, и в нем также переопределить метод Print.
При переопределении виртуальных методов следует учитывать ряд ограничений:
Виртуальный и переопределенный методы должны иметь один и тот же модификатор доступа. То есть если виртуальный метод определен с помощью модификатора public, то и переопредленный метод также должен иметь модификатор public.
Нельзя переопределить или объявить виртуальным статический метод.
Ключевое слово base
Кроме конструкторов, мы можем обратиться с помощью ключевого слова base к другим членам базового класса. В нашем случае вызов base.Print(); будет обращением к методу Print() в классе Person:
Переопределение свойств
Также как и методы, можно переопределять свойства:
В данном случае в классе Person определено виртуальное свойство Age, которое устанавливает значение, если оно больше 0 и меньше 110. В классе Employee это свойство переопределено - возраст работника должен быть не меньше 18.
Запрет переопределения методов
Также можно запретить переопределение методов и свойств. В этом случае их надо объявлять с модификатором sealed :
При создании методов с модификатором sealed надо учитывать, что sealed применяется в паре с override, то есть только в переопределяемых методах.
И в этом случае мы не сможем переопределить метод Print в классе, унаследованном от Employee.
В прошлой теме были определены следующие классы Person и Employee:
Класс Employee наследует функцию display и может использовать ее для вывода информации о сотруднике. Однако реализация этой функции в классе Person выводит на консоль только имя и возраст. Тогда как в классе Employee определена дополнительная переменная - company, которая хранит компанию, в которой работает объект Employee. И было бы хорошо, если бы для объекта Employee функция display также выводила бы значения переменной company.
Мы можем решить эту проблему с помощью виртуальных функций. То есть чтобы в производном классе можно было бы переопределить функцию, в базовом классе подобная функция должна быть объявлена с ключевым словом virtual :
Теперь функция display может быть переопределена в производных классах. Причем производный класс может и не переопределять функцию, а использовать унаследованный функционал.
Для переопределения виртуальной функции в производном классе она должна быть определена с ключевым словом override , которое помещается после списка параметров функции:
Класс, который определяет или наследует виртуальную функцию, еще назвается полиморфным (polymorphic class). То есть в данном случае Person и Employee являются полиморфными классами.
При переопределении виртуальных функций следует учитывать, что переопределенная версия функции в производном классе должна иметь тот же набор параметров и возвращать объект того же типа, что и виртуальная функция в базовом классе.
Обращение к базовому классу
Теперь в классе Employee функция display выводит имя и компанию сотрудника. Однако функция display в классе Person выводит еще и возраст человека. Но поскольку переменная age в классе Person определена как закрытая, то класс Employee не имеет к ней доступа.
Мы могли бы в данном случае определить переменную age как защищенную, то есть со спецификатором protected, однако есть и другой способ - мы можем просто обратиться к реализации функции базового класса. Применим данный способ:
С помощью вызова Person::display(); будет выполняться реализация функции display из класса Person, которая выведет на консоль имя и возраст. Соответственно нам остается только определить код для вывода компании. Причем таким образом мы можем обращаться к любому функционалу базового класса за исключением приватных переменных и функций.
Применим классы в программе:
Консольный вывод программы:
Также мы можем вызывать реализацию из базового класса через переменную производного класса:
Сокрытие функций
Производный класс может определить функцию с тем же именем, что и виртуальная функция в базовом классе, с тем же или другим списком параметров. Для компилятора такая функция будет существовать независимо от базового класса. И подобное определение функции в производном классе не будет переопределением функции из базового класса.
В данном случае функция display представляет совершенно новую реализацию, которая не имеет ничего общего с базовым классом Person. Она скрывает по сути виртуальную функцию из базового класса.
Нередко подобные функции являются результатом невнимательности - хотели переопределить функцию базового класса, но забыли поставить спецификатор override. В то же время ошибки в данном случае не будет. И мы также сможем использовать данную функцию:
Запрет переопределения
С помощью спецификатора final мы можем запретить определение в производных классах функций, которые имеют то же самое имя, возвращаемый тип и список параметров, что и виртуальная функция в базовом классе. Например:
Читайте также: