Как правильно писать тесты 46 Цикл разработки 46 Структура проекта с тестами 51 Утверждения (Asserts) 52 Утверждения в форме ограничений 54 Категории 56

Вид материалаТесты

Содержание


Метод ToString()
Подобный материал:
1   ...   12   13   14   15   16   17   18   19   ...   47

Метод ToString()




Метод System.Object.ToString() является одним из наиболее используемых в среде .NET. Необходимо при разработке своего класса переопределять этот метод. В противном случае каждому пользователю класса придется самостоятельно извлекать свойства класса и создавать из них читабельную строку.

Информация о классе в виде строки может быть полезна при использовании Windows Forms, Web forms или при выводе на экран. Кроме того, строковое представление может быть полезно при отладке. При создании сложных классов следует использовать более хитроумный метод IFormattable.ToString(). Помните: если этот метод не будет переопределен или если переопределение будет плохо написано, за разработчиков это придется делать самим клиентам.


Версия метода ToString() из System.Object просто возвращает имя класса. Эта информация бесполезна: ведь "Rect", "Point" или "Size" — вовсе не то, что следует показывать пользователям. Но это именно то, что будут получать пользователи, если в ваших классах не будет переопределен метод ToString(). Класс пишется всего один раз, но использовать его клиентам приходится много раз. Чуть больше работы при написании класса, но это окупится при его применении.


Обсудим самое простое из требований: переопределение System.Object.ToString(). Рассмотрим класс Customer с тремя полями:

public class Customer

{

private string _name;

private decimal _revenue;

private string _contactPhone;

}


Версия из Object.ToString() вернет "Customer". Лучше переопределить метод, чтобы он возвращал хотя бы имя заказчика:

public override string ToString()

{

return _name;

}


Еще одна причина, по которой удобно переопределять этот метод, состоит в том, что .NET FCL использует его при выводе названий объектов, помещенных в визуальные элементы управления (combo boxes, list boxes, text boxes). Этот же метод работает при передаче объекта в методы

System. Console.WriteLine() и System.String.Format().


В приведенном выше типе customer имеются три поля: имя пользователя, его доход и номер контактного телефона. В переопределении System.ToString() применяется только имя заказчика. Эту ситуацию можно исправить, включив в используемый тип реализацию интерфейса IFormattable. В IFormattable содержится метод ToString(), позволяющий задавать для типа форматирующую информацию. Именно этот интерфейс следует применять в тех случаях, когда требуется создать различные формы строкового вывода. Класс customer относится к подобным случаям. Пользователь может захотеть создать отчет, в котором будет в табличной форме содержаться информация об имени заказчика и его доходе за последний год. Метод IFormattable.ToString() предлагает средства, позволяющие форматировать строку, получаемую от метода ToString(). Сигнатура метода IFormattable.ToString() имеет в параметрах строку с форматом и провайдера формата:


string System.IFormattable.ToString( string format, IFormatProvider formatProvider )


С помощью параметра format можно задать собственные форматы для строки, описывающей класс. Можно задать собственные ключевые символы. В примере с customer можно указать, что n означает имя, r — доходы, p — номер телефона. Если разрешить пользователям применение комбинаций символов, можно создать такую версию

#region IFormattable Members

// supported formats:

// substitute n for name.

// substitute r for revenue

// substitute p for contact phone.

// Combos are supported: nr, np, npr, etc

// "G" is general.

string System.IFormattable.ToString( string format,

IFormatProvider formatProvider )

{

if ( formatProvider != null )

{

ICustomFormatter fmt = formatProvider.GetFormat(

this.GetType( ) )

as ICustomFormatter;

if ( fmt != null )

return fmt.Format( format, this, formatProvider );

}


switch ( format )

{

case "r":

return _revenue.ToString( );

case "p":

return _contactPhone;

case "nr":

return string.Format( "{0,20}, {1,10:C}",

_name, _revenue );

case "np":

return string.Format( "{0,20}, {1,15}",

_name, _contactPhone );

case "pr":

return string.Format( "{0,15}, {1,10:C}",

_contactPhone, _revenue );

case "pn":

return string.Format( "{0,15}, {1,20}",

_contactPhone, _name );

case "rn":

return string.Format( "{0,10:C}, {1,20}",

_revenue, _name );

case "rp":

return string.Format( "{0,10:C}, {1,20}",

_revenue, _contactPhone );

case "nrp":

return string.Format( "{0,20}, {1,10:C}, {2,15}",

_name, _revenue, _contactPhone );

case "npr":

return string.Format( "{0,20}, {1,15}, {2,10:C}",

_name, _contactPhone, _revenue );

case "pnr":

return string.Format( "{0,15}, {1,20}, {2,10:C}",

_contactPhone, _name, _revenue );

case "prn":

return string.Format( "{0,15}, {1,10:C}, {2,15}",

_contactPhone, _revenue, _name );

case "rpn":

return string.Format( "{0,10:C}, {1,15}, {2,20}",

_revenue, _contactPhone, _name );

case "rnp":

return string.Format( "{0,10:C}, {1,20}, {2,15}",

_revenue, _name, _contactPhone );

case "n":

case "G":

default:

return _name;

}

}

#endregion


Воспользоваться этим методом можно так:

IFormattable c1 = new Customer();

Console.WriteLine( "Customer record: {0}",

c1.ToString( "nrp", null ) );

Реализация IFormattable.ToString() зависит от конкретного класса, но ряд случаев необходимо обрабатывать в каждой реализации интерфейса IFormattable. Во-первых, необходимо поддерживать общий формат "G". Во-вторых, нужно поддерживать пустой формат в обоих вариантах его задания: "" и null. Все три спецификатора формата должны возвращать ту же самую строку, что и переопределяемый нами метод Object.ToString(). Для каждого типа, в котором реализуется IFormattable, .NET FCL вызывает IFormattable.ToString() вместо Object.ToString(). Обычно .NET FCL вызывает IFormattable. ToString с пустой (null) строкой формата, но в некоторых случаях для указания общего формата используется строка формата "G". Если добавить поддержку интерфейса IFormattable и не обеспечить поддержку этих стандартных форматов, будет нарушено автоматическое преобразование строк в FCL.


Второй параметр в IFormattable.ToString() является объектом, который реализует интерфейс IFormatProvider. Этот параметр позволяет клиентам добавлять не предусмотренные при проектировании опции форматирования. В предложенной выше реализации IFormattable.ToString() отсутствует целый ряд опций форматирования, которые могли бы быть полезны. Рано или поздно пользователю потребуется такой формат, предусмотреть который не удалось. Вот почему в нескольких первых строках метода ищется объект, который реализует IFormatProvider, и выполняется делегирование задания в ICustomFormatter.

Допустим, что потребовался формат, не поддерживаемый на данный момент. Скажем, появились заказчики, чьи фамилии длиннее 20 символов, и нужно модифицировать формат так, чтобы предоставить фамилии заказчика поле размером 50 символов. Здесь необходим интерфейс IFormatProvider. Для получения собственных форматов выходных данных вы создаете класс, в котором реализуется IFormatProvider, и класс-компаньон, в котором реализуется ICustomFormatter. В интерфейсе IFormatProvider определяется один метод: GetFormat(). Этот метод возвращает объект, реализующий интерфейс ICustomFormatter. А этот интерфейс специфицирует метод, который выполняет фактическое форматирование. Следующая пара (классов) создает модифицированные выходные данные, где имени пользователя отводится 50 позиций:

// Example IFormatProvider:

public class CustomFormatter : IFormatProvider

{

#region IFormatProvider Members

// IFormatProvider contains one method.

// This method returns an object that

// formats using the requested interface.

// Typically, only the ICustomFormatter

// is implemented

public object GetFormat( Type formatType )

{

if ( formatType == typeof( ICustomFormatter ))

return new CustomerFormatProvider( );

return null;

}

#endregion


// Nested class to provide the

// custom formatting for the Customer class.

private class CustomerFormatProvider : ICustomFormatter

{

#region ICustomFormatter Members

public string Format( string format, object arg,

IFormatProvider formatProvider )

{

Customer c = arg as Customer;

if ( c == null )

return arg.ToString( );

return string.Format( "{0,50}, {1,15}, {2,10:C}",

c.Name, c.ContactPhone, c.Revenue );

}

#endregion

}

}


Метод GetFormat() создает объект, чей класс реализует интерфейс ICustomFormatter. Метод ICustomFormatter.Format() выполняет всю работу по форматированию.

В ICustomFormatter.Format() можно задать параметры форматирования. В результате объект FormatProvider типа IFormatProvider будет получен из метода GetFormat(). Чтобы задать свой способ форматирования при выводе информации об объекте, нужно передать в метод string.Format() объект типа IFormatProvider:

Console.WriteLine( string.Format( new CustomFormatter(), "", c1 ));



Содержание 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 260

Создание обобщенных методов 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