Как правильно писать тесты 46 Цикл разработки 46 Структура проекта с тестами 51 Утверждения (Asserts) 52 Утверждения в форме ограничений 54 Категории 56
Вид материала | Тесты |
СодержаниеОтличие реализации методов интерфейса от переопределения виртуальных методов |
- Некорректные задания, 1276.79kb.
- К техническому регламенту, 835.7kb.
- Правительства Российской Федерации от 11 ноября 2005 г. N 679 о порядке разработки, 494.44kb.
- Постановлением Правительства Российской Федерации от 11 ноября 2005 г. N 679 о порядке, 652.85kb.
- Постановлением Правительства Российской Федерации от 11 ноября 2005 г. N 679 о порядке, 623.18kb.
- Правительства Российской Федерации от 11. 11. 2005 N 679 о порядке разработки и утверждения, 533.6kb.
- Постановления Правительства Российской Федерации от 11. 11. 2005 n 679 о порядке разработки, 613.63kb.
- Об утверждении требований к схемам теплоснабжения, порядку их разработки и утверждения, 450.79kb.
- Рабочая программа учебной дисциплины. Общие требования, порядок разработки, согласования, 414.77kb.
- Постановлением Правительства Российской Федерации от 11 ноября 2005 г. N 679 о порядке, 1924.26kb.
Отличие реализации методов интерфейса от переопределения виртуальных методов
На первый взгляд кажется, что реализация метода интерфейса — это то же самое, что и переопределение виртуального метода. Вы предлагаете определение члена, который был объявлен в другом типе. Однако реализация интерфейса значительно отличается от переопределения виртуального метода. Члены, объявленные в интерфейсах, не виртуальны — по крайней мере, по умолчанию.
Производные классы не могут переопределять члены интерфейса, реализованные в базовом классе.
Но можно реализовать интерфейсы таким образом, чтобы производные классы могли модифицировать вашу реализацию. Для этого всего лишь требуется создать методы для производных классов.
Рассмотрим простой интерфейс и его реализацию в одном классе:
interface IMsg
{
void Message();
}
public class MyClass : IMsg
{
public void Message()
{
Console.WriteLine( "MyClass" );
}
}
Метод Message() является частью общедоступного интерфейса MyClass. К методу Message можно также получить доступ через переменную типа IMsg. Теперь немного усложним ситуацию, добавив производный класс:
public class MyDerivedClass : MyClass
{
public new void Message()
{
Console.WriteLine( "MyDerivedClass" );
}
}
Обратите внимание, что нужно добавить в определение метода Message() ключевое слово new. Метод MyClass.Message() не является виртуальным. Производные классы не могут переопределить этот метод Message(). Поэтому MyDerivedClass создает новый метод Message, но он не переопределяет MyClass.Message: он скрывает его. Более того, метод MyClass.Message остается доступным по ссылке типа IMsg:
MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyClass"
Интерфейсные методы не являются виртуальными. Когда реализуется интерфейс, объявляется точная реализация конкретного соглашения.
Но часто возникает желание создать интерфейсы, реализовать их в базовых классах, а затем модифицировать поведение в производных классах. Это возможно, причем имеются два варианта. Если доступ к базовому классу отсутствует, можно заново реализовать интерфейс в производном классе:
public class MyDerivedClass : MyClass, IMsg
{
public new void Message()
{
Console.WriteLine( "MyDerivedClass" );
}
}
Добавление реализации IMsg изменяет поведение производного класса таким образом, что теперь IMsg.Message() использует версию производного класса:
MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyDerivedClass"
По-прежнему в методе MyDerivedClass.Message() требуется ключевое слово new. Версия базового класса все еще доступна по ссылке на этот базовый класс:
MyDerivedClass d = new MyDerivedClass( );
d.Message( ); // prints "MyDerivedClass".
IMsg m = d as IMsg;
m.Message( ); // prints "MyDerivedClass"
MyClass b = d;
b.Message( ); // prints "MyClass"
Единственный способ исправить эту проблему — модифицировать базовый класс, объявив, что интерфейсные методы должны быть виртуальными:
public class MyClass : IMsg
{
public virtual void Message()
{
Console.WriteLine( "MyClass" );
}
}
public class MyDerivedClass : MyClass
{
public override void Message()
{
Console.WriteLine( "MyDerivedClass" );
}
}
Теперь класс MyDerivedClass и все классы, производные от MyClass, могут объявить для Message() собственные методы. После этого каждый раз будет вызываться переопределенная версия: по ссылке на MyDerivedClass, по ссылке IMsg и по ссылке на MyClass.
Если реализация метода интерфейса не нужна в базовом классе, внесите в определение MyClass одно небольшое изменение:
public abstract class MyClass: IMsg
{
public abstract void Message();
}
Т.е. можно реализовать интерфейс без фактической реализации методов в этом интерфейсе. Объявляя абстрактные версии методов в интерфейсе, вы говорите, что все типы, являющиеся производными от вашего типа, должны реализовать этот интерфейс. Интерфейс IMsg является частью объявления класса MyClass, но определение методов отложено до каждого производного класса.
Явная реализация методов интерфейса используется для того, чтобы не позволить клиентскому коду применять интерфейсные методы, когда имеется более подходящая версия.
Реализация методов интерфейсов предоставляет больше возможностей, чем создание и переопределение виртуальных функций. Можно создавать private реализации, виртуальные реализации или абстрактные соглашения при построении иерархий классов. Вы можете решить, как и когда производные классы могут модифицировать поведение по умолчанию членов любого интерфейса, реализованного классом.
Содержание 1
Процесс разработки программного обеспечения 3
Основные принципы объектно-ориентированного программирования (начало) 12
История 14
Основные принципы объектно-ориентированного программирования (продолжение) 15
Главные понятия 15
Основные принципы 15
Абстракция данных 15
Инкапсуляция 16
Наследование 16
Основные принципы объектно-ориентированного программирования (окончание) 18
Полиморфизм 18
Отношения 19
Основы .NET Framework 22
Введение 22
Обзор выполнения кода в среде CLR 26
Компиляция исходного кода в управляемые модули 26
Части управляемого модуля 28
Объединение управляемых модулей в сборку 29
Загрузка CLR при выполнении программы 31
Исполнение кода сборки 31
IL и верификация 36
Небезопасный код 36
IL и защита интеллектуальной собственности 37
NGen.exe — генератор объектного кода 38
Библиотека классов .NET Framework 38
Общая система типов (Common Type System, CTS) 40
Общеязыковая спецификация 42
Модульное тестирование (unit testing) 44
Предпосылки 44
Преимущества 44
Поощрение изменений 45
Упрощение интеграции 45
Документирование кода 45
Отделение интерфейса от реализации 45
Ограничения 46
Как правильно писать тесты 46
Цикл разработки 46
Структура проекта с тестами 51
Утверждения (Asserts) 52
Утверждения в форме ограничений 54
Категории 56
Настройка среды выполнения тестов 57
Дополнительные утверждения 59
Тесты и исключения 60
Правила тестирования 62
Юнит-тестирование 70
Пример 70
Два вида констант 98
Операторы is, as и приведение типов 101
Метод ToString() 109
Типы-значения и ссылочные типы 117
Неизменяемые (Immutable) атомарные типы-значения 122
0 в типах-значениях 132
Методы ReferenceEquals(), Equals(), статический метод Equals() и operator== 134
Циклы foreach 140
Управление ресурсами в .NET 143
Инициализаторы переменных 149
Инициализация статических полей классов с помощью статических конструкторов 155
Цепочки конструкторов 157
Применение операторов using и try/finally для освобождения ресурсов 160
О минимизации мусора 167
Упаковка и распаковка 169
Наследование классов и реализация интерфейсов 171
Отличие реализации методов интерфейса от переопределения виртуальных методов 178
Введение в паттерны проектирования 184
Что такое паттерн проектирования 186
Паттерны проектирования в схеме MVC 188
Описание паттернов проектирования 190
Каталог паттернов проектирования 192
Как решать задачи проектирования с помощью паттернов 195
Механизмы повторного использования 204
Сравнение структур времени выполнения и времени компиляции 210
Проектирование с учетом будущих изменений 212
Как выбирать паттерн проектирования 220
Как пользоваться паттерном проектирования 221
Делегаты и события 226
Делегаты 226
События 230
Параметры событий 233
Атрибуты 237
Синтаксис 237
Создание атрибута 240
Составляющие класса атрибута 242
Получение значений атрибута 244
Задание 1 245
Задание 2 246
Обобщения 251
Проблемы создания объектных образов и восстановления значений 252
Типовая безопасность и строго типизованные коллекции 253
Проблемы создания объектных образов и строго типизованные коллекции 258
Пространство имен System.Collections.Generic 259
Тип List
Создание обобщенных методов 263
Пропуск параметров типа 265
Создание обобщенных структур (и классов) 267
Ключевое слово default в обобщенном программном коде 269
Создание пользовательских обобщенных коллекций 271
Установка ограничений для параметров типа с помощью where 274
Отсутствие поддержки ограничений при использовании операций 279
Создание обобщенных базовых классов 280
Создание обобщенных интерфейсов 282
Создание обобщенных делегатов 284
Несколько слов о вложенных делегатах 286
Задачи 286
Примеры реализации по шаблонам Мост+Фабрика 289
Пример 1 289
Пример 2 292
Пример 3. Контроллер в виде интерфейса, а не класса. 294
Различное поведение фабрик 298
Фабрики для создания плагинов 299
Фабрики для создания объектов по некоторому алгоритму 300
Фабрики для клонирования объектов 300
Некоторые замечания об использовании свойств и наследования 302
Замечания об использовании свойств 302
Замечания об использовании наследования. Проблема «хрупкого» базового класса. 304