Главная » C#, Компоненты » Преимущества модели провайдеров

0

За время развития языков программирования были сделаны разные попытки хранить данные о расположении элементов. В первых версиях Delphi это были dfm-файлы бинарного вида, хранящие информацию времени выполнения, В более поздних версиях появилась возможность хранить такие файлы в текстовом внде. В Visual Basic такая информация хранилась в специальных frm-файлах н т. д.

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

Среда Visual Studio .NET использует очень интересный способ хранения информации времени выполнения: данные о компонентах сериализуются в код, что мы и видели в примере, который я показывал в начале этого раздела. Таким образом, информация хранится в текстовом виде, ее просто редактировать, и, т. к. в конечном итоге получается скомпилированный код, не тратится время на интерпретацию информации во время выполнения. Конечно, некоторое время тратится на преобразование кода в форму при открытии формы в режиме дизайнера, но это время невелико и не столь критично, как затраты времени выполнения.

Принцип работы провайдеров

Создание кода, описывающего форму, производится в два этапа. Первый этап заключается в создании "дерева кода", представляющего собой описание кода в виде объектов. Такое описание не зависит от конкретного языка программирования. После этого, на втором этапе, провайдер конкретного языка генерирует код на нужном языке программирования. Понятно, что если мы не собираемся писать собственный язык программирования, то вмешиваться в кодогенерацию не стоит, поэтому дальше мы будем обсуждать только первую часть процесса.

Итак, создание "дерева" происходит следующим образом:

1.         Создается CodeDom-генератор нужного языка программирования (в нашем случае это С#).

2.   Сериапизатор перебирает все публичные (public) свойства компонента.

3.   Определяет, подлежит ли свойство сериал изации.

4.         Если да, то сериализует содержимое свойства в дерево и передает его CodeDom-генератору.

Если свойство является простым типом, то проблем с сериал изацией нет. Если свойство является компонентом (точнее сказать реализует интерфейс

IComponent), то процесс может повторяться рекурсивно. В случае, когда свойство не является ни простым типом, ни компонентом, дело обстоит сложнее. В этом случае среде нужно каким-то образом суметь сериапизовать свойство, о котором она не имеет фактически никакой информации. Для этого используется конвертер типа (см. главу 8), позволяющий преобразовать исходный тип К типу Instance Descriptor.

Для демонстрации последней проблемы сделаем компонент Testcomponent, который содержит СВОЙСТВО Location типа LocationType, которое, в свою очередь, содержит поле типа Point (листинг 10.1). Для того чтобы поле можно было редактировать, мы подключим к нему стандартный конвертер ExpandabieObjectconverter (см. разд. 8.9). Добавьте компонент иа форму и загляните в получившийся код. Никакой информации о свойстве Location не сохранилось. Как я уже говорил, это происходит потому, что среда разработки не знает, каким образом нужно работать с классом LocationType.

Теперь подключите к классу LocationType конвертер, приведенный в листинге 10.2. Свойство будет сохранено в коде:

this.testComponentl.Location =

new SerializeTest.LocationType(new System.Drawing.Point(5, 5));

Основная работа конвертера происходит в методе convertToO, который производит преобразование исходного типа в тип InstanceDescriptor:

Constructorlnfo ci = typeof(LocationType).GetConstructor(

new Type[j ( typeof(Point) }); LocationType t = (LocationType)value;

return new InstanceDescriptor(ci, new objectf) { t.Location });

Сначала мы получаем конструктор, который позволит нам создать объект нужного типа. В данном случае мы выбираем конструктор с одним параметром, но в общем случае может потребоваться выбирать один из нескольких конструкторов, в зависимости от значений полей объекта или, например, текущей культуры (параметр culture). Затем, на основе полей исходного объекта value МЫ создаем экземпляр класса InstanceDescriptor, который И позволяет Visual Studio сериапизовать поле Location в код. Теперь посмотрим, как можно управлять сериализацией полей классов.

Листинг 10 1 Класс с нссериализуемым полем

using System;

using System.ComponentModel;

using System.ComponentModel.Design.Serialization;

using System.Reflection; using Syston. Drawing; using System.Globalization;

namespace SerializeTest

}

11 Тестовый компонент со свойстаом типа LocationType, it внутри которого есть свойство типа Point

public class TestComponent : Component {

LocationType location;

public TestComponent()

{

location = new LocationType(new Point(5, 5));

}

public LocationType Location

{

get

{

return location;

}

set

{

location = value;

}

}

}

it Без подключения конвертера свойства этого типа не будут // сериализованы

[TypeConverter(typeof[LocationClassConverter))] public class LocationType )

Point location;

public Point Location

{

get

{

return location;

set

{

location = value;

}

}

public LocatiorvType (Poirvt location)

{

this.location ~ location;

}

)

}

Листинг 10 2. Класс коньсртера типа LocationTyp»

class LocationClassConverter : TypeConverter

{

public override bool GetPropertiesSupported(

ITypeDescriptorContext context)

{

return true;

}

public override PropertyDescriptorCollection GetProperties ( ITypeDescriptorContext context, object value. Attribute[] attributes)

{

return TypeDescriptor.GetProperties(typeof(LocationType));

}

// Возвращает возможность конвертирования public override bool CanConvertTo(

ITypeDescriptorContext context, Type destinationType)

{

// Для InstanceDescriptor разрешаем конвертирование if (destinationType == typeof(InstanceDescriptor))

{

return true;

}

return base.CanConvertTofcontext, destinationType);

// Производит конвертирование

public override object ConvertTo(ITypeDescriptorContext context, Culturelnfo culture, object value, Type destinationType)

{

if (destinationType == typeof(instanceDescriptor)) (

// Ищем конструктор с параметром Point Constructorlnfo ci =

typeof(LocationType).GetConstructor( new Type[] ( typeof(Point) )); // Объект, который будет сериализовываться LocationType t = (LocationType)value; // Создаем InstanceDescriptor

return new InstanceDescriptor(ci, new object[] ( t.Location });

}

return base.ConvertTo(context, culture, value, destinationType);

}

}

Литература:

Агуров П. В. C#. Разработка компонентов в MS Visual Studio 2005/2008. – СПб.: БХВ-Петербург, 2008. — 480 е.: ил.

По теме:

  • Комментарии