Пример декларативного программирования

Рассмотрим этот механизм на примере работы процедуры протоколирования, приведенной в подразделе "Трассировка полей объекта". В своем простейшем виде она выводит абсолютно все поля объекта без разбора. С помощью атрибутов можно создать механизм управления выводом в log тех или иных полей класса (фильтрация). Для этого достаточно ввести новый атрибут, обозначающий, что протоколирование данного поля не требуется. Ниже приведено описание атрибута NotTrace:

 

[AttributeUsage(AttributeTargets.Field) ]

public class NotTrace : Attribute

{

}

 

Чтобы упростить использование этого атрибута, напишем простую вспомогательную функцию:

 

static bool NeedTrace(FieldInfo fi)

{ return ! Attribute.IsDefined(fi, typeof(NotTrace));

}

 

Теперь можно модифицировать функцию протоколирования, чтобы она выводила только те поля, у которых не задан атрибут NotTrace:

 

foreach (FieldInfo fi in type.GetFields(...))

{ if (! NeedTrace(fi))

continue;

...

}

 

Теперь управление выводом полей производится не изменением кода функции протоколирования, а изменением описаний полей – заданием их атрибутов. Другими словами, судьба объекта задаётся при его декларациидекларативное программирование.

Порядок выполнения работы

В данном порядке выполнения работы рассмотрено создание приложения для просмотра содержимого сборки. Приложение будет выводить на экран интерфейсы и классы сборки, конструкторы, методы и поля классов, передаваемые и возвращаемые параметры методов.

1. Создайте новое приложение Windows Forms. Как создавать приложение, описано в порядке выполнения работы лабораторной работы № 1.

2. Спроектируйте интерфейс пользователя: разместите на форме лавное меню (компонент MenuStrip), добавьте пункт меню «Файл», к пункту меню файл добавьте пункт меню «Открыть сборку».

3. Разместите на форме компонент TreeView и разверните его по размерам формы, добавьте компонент ImageList, свяжите компонент ImageList с TreeView (свойство ImageList), добавьте в него несколько картинок для узлов дерева, добавьте компонент OpenFileDialog.

4. Откройте форму в режиме редактора CSharp, добавьте методы для открытия произвольной сборки с использованием OpenFileDialog:

 

//Получение пути к сборке через OpenFileDialog

private string selectAssemblyFile()

{

openFileDialog1.Filter = "Dll files (*.dll)|*.dll|Exe files

(*.exe)|*.exe| All files (*.*)|*.*";

openFileDialog1.Title = "Select assembly file";

return (openFileDialog1.ShowDialog() == DialogResult.OK) ?

openFileDialog1.FileName : null;

}

 

//Загрузка сборки

private Assembly openAssembly(string path)

{

try

{

Assembly a = Assembly.LoadFrom(path);

return a;

}

catch (Exception)

{

MessageBox.Show("Не удалось загрузить указанную сборку!",

"Ошибка!", MessageBoxButtons.OK, MessageBoxIcon.Error);

return null;

}

}

 

5. Добавьте ссылку на сборку над конструктором формы:

 

private Assembly assembly;

 

6. Добавьте методы по добавлению в дерево всех классов и интерфейсов, а также полей, конструкторов и методов класса:

 

//Добавить все классы и интерфейсы сборки к узлу дерева

void addRoot(TreeNode root, Type[] types)

{

TreeNode node = null;

foreach (Type type in types)

{

node = new TreeNode();

node.Text = type.ToString();

//Если класс

if (type.IsClass)

{

node.ImageIndex = 1;

node.SelectedImageIndex = 1;

addFirstLevel(node, type);

root.Nodes.Add(node);

}

//Если интерфейс

else if (type.IsInterface)

{

node.ImageIndex = 2;

node.SelectedImageIndex = 2;

addFirstLevel(node, type);

root.Nodes.Add(node);

}

}

}

 

//Загрузить все поля, конструкторы и методы

private void addFirstLevel(TreeNode node, Type type)

{

TreeNode node1 = null;

 

FieldInfo[] fields = type.GetFields();

MethodInfo[] methods = type.GetMethods();

ConstructorInfo[] constructors = type.GetConstructors();

 

//Загрузить поля

foreach (FieldInfo field in fields)

{

node1 = new TreeNode();

node1.Text = field.FieldType.Name + " " + field.Name;

node1.ImageIndex = 5;

node1.SelectedImageIndex = 5;

node.Nodes.Add(node1);

}

 

//Загрузить конструкторы

foreach (ConstructorInfo constructor in constructors)

{

String s = "";

ParameterInfo[] parametrs = constructor.GetParameters();

foreach (ParameterInfo parametr in parametrs)

{

s = s + parametr.ParameterType.Name + ", ";

}

s = s.Trim();

s = s.TrimEnd(',');

node1 = new TreeNode();

node1.Text = node.Text + "(" + s + ")";

node1.ImageIndex = 6;

node1.SelectedImageIndex = 6;

node.Nodes.Add(node1);

}

 

//Загрузить методы

foreach (MethodInfo method in methods)

{

String s = "";

ParameterInfo[] parametrs = method.GetParameters();

foreach (ParameterInfo parametr in parametrs)

{

s = s + parametr.ParameterType.Name + ", ";

}

s = s.Trim();

s = s.TrimEnd(',');

node1 = new TreeNode();

node1.Text = method.ReturnType.Name + " " + method.Name + "("

+ s + ")";

node1.ImageIndex = 4;

node1.SelectedImageIndex = 4;

node.Nodes.Add(node1);

}

}

 

7. Добавьте обработчик нажатия на пункт меню «Открыть сборку»:

 

private void открытьСборкуToolStripMenuItem_Click(object sender,

EventArgs e)

{

treeView1.Nodes.Clear();

string path = selectAssemblyFile();

if (path != null)

{

assembly = openAssembly(path);

}

if (assembly != null)

{

TreeNode root = new TreeNode();

root.Text = assembly.GetName().Name;

root.ImageIndex = 0;

root.SelectedImageIndex = 0;

treeView1.Nodes.Add(root);

Type[] types = assembly.GetTypes();

addRoot(root, types);

}

}

 

8. Запустите проект и попробуйте открыть сборку. Можно, например, открыть сборку c каталога WINDOWS\Microsoft.NET\Framework.

Внешний вид программы представлен на рисунке 2.1.

Рисунок 2.1 – Внешний вид интерфейса программы

Задание на лабораторную работу

Доработать приложение, описанное в порядке выполнения работы, чтобы в компоненте TreeView отображались дополнительно перечисления и структуры, а также не только публичные поля классов, но и приватные. Также необходимо чтобы при нажатии на любой класс, интерфейс, структуру или перечисление в компоненте ListView отображалась подробная информация о них. При нажатии на метод, конструктор или поле необходимо выводить в ListView типы полей, входные и возвращаемые параметры методов и параметры конструкторов.

Следует обратить внимание на разработку интерфейса приложения. Интерфейс не должен быть переполнен элементами управления, но в тоже время должен предоставлять наибольшую функциональность.

Содержание отчета

- фамилия и имя исполнителя лабораторной работы;

- номер и название лабораторной работы;

- цель лабораторной работы;

- краткие теоретические сведенья на одну страницу;

- ход работы (листинги программ, скриншоты программ);

- выводы о проделанной работе.

Контрольные вопросы

1. Что такое сборка?

2. Из чего состоит сборка?

3. Что такое рефлексия?

4. Как загрузить сборку?

5. Как получить данные о типах сборки?

6. Чем отличается сборка с расширением exe от сборки с расширением dll?


3 Лабораторная работа № 3
ADO.NET

Цель работы

Изучить возможности доступа к базам данных, используя технологию ADO.NET, получить практические навыки по работе с классами ADO.NET.

Теоретические сведенья

Общие сведения об ADO.NET

ADO.NET(ActiveX Data Object .NET) – набор классов, используемый для доступа к источникам данных в платформе .NET. ADO.NET поддерживает асинхронный доступ к данным и сериализацию данных, получаемых из хранилища, в формате XML.

ADO.NET не предлагает единого набора типов для связи со всеми СУБД. В пространстве имен System.Data.Common находятся базовые классы и интерфейсы, а соответствующие производные типы для разных поставщиков данных – в одноименных пространствах System.Data.SqlClient, System.Data.Odbc и т.п.

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

Библиотеки ADO.NET могут использоваться в рамках двух концептуально различных способах взаимодействия: на связном уровне, для чего используются классы подсоединенных объектов или несвязном уровне, где нужны классы отсоединенных объектов (рис. 3.1).

Рисунок 3.1 – Организация классов ADO.NET

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