Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции OOP c#.doc
Скачиваний:
44
Добавлен:
22.09.2019
Размер:
3.38 Mб
Скачать

2. Базовые элементы .Net Framework

2.1. Метаданные и механизм отражения

При программировании для платформы .NET в любую сборку помещаются метаданные, которые являются описанием всех типов данных сборки и их членов. Работа с метаданными происходит при помощи специального механизма, называемого механизмом отражения (reflection).

Главные элементы, которые необходимы для использования возможностей отражения – это класс System.Type и типы из пространства имен System.Reflection.

Класс System.Type служит для получения информации о типе. Для того чтобы получить объект данного класса, существует несколько возможностей.

1. Вызвать у объекта метод GetType(). Данный метод определен на уровне System.Object, а значит присутствует у любого объекта:

Foo A = new Foo(); //Foo – это некий класс

Type t = A.GetType();

2. Использовать статический метод Type.GetType(), которому передается текстовое имя интересующего нас типа:

Type t;

t = Type.GetType("Foo");

3. Использовать ключевое слово языка C# typeof:

Type t = typeof(Foo);

Чтобы продемонстрировать возможности класса Type, опишем вспомогательный класс Foo.

public class Foo {

private int Field1;

public float Field2;

private int Method1() {

Console.WriteLine("Method1 is called");

return 0;

}

public void Method2(string s, ref int i) {

Console.WriteLine("Method2 is called");

Console.WriteLine("First parameter is " + s);

Console.WriteLine("Second parameter is " + i);

}

public int Prop {

get { return Field1; }

set { Field1 = value; }

}

}

А теперь рассмотрим код, который выводит информацию об элементах типа Foo (для использования типов, подобных FieldInfo, необходимо подключить пространство имен System.Reflection).

Type t = typeof(Foo);

//Это общая информация

Console.WriteLine("Full name = " + t.FullName);

Console.WriteLine("Base is = " + t.BaseType);

Console.WriteLine("Is abstract = " + t.IsAbstract);

Console.WriteLine("Is sealed = " + t.IsSealed);

Console.WriteLine("Is class = " + t.IsClass);

Console.WriteLine("******************************");

//Сейчас пройдемся по полям

FieldInfo[] fi = t.GetFields();

foreach(FieldInfo f in fi)

Console.WriteLine("Field = " + f.Name);

Console.WriteLine("******************************");

//А теперь по свойствам

PropertyInfo[] pi = t.GetProperties();

foreach(PropertyInfo p in pi)

Console.WriteLine("Property = " + p.Name);

Console.WriteLine("******************************");

//С методами поработаем подробнее

MethodInfo[] mi = t.GetMethods();

foreach(MethodInfo m in mi) {

Console.WriteLine("Method Name = " + m.Name);

Console.WriteLine("Method Return Type = " + m.ReturnType);

//Изучим параметры метода

ParameterInfo[] pri = m.GetParameters();

foreach(ParameterInfo pr in pri) {

Console.WriteLine("Parameter Name = " + pr.Name);

Console.WriteLine("Type = " + pr.ParameterType);

}

Console.WriteLine("******************************");

}

Данный код выводит следующую информацию:

Full name = refl.Foo

Base is = System.Object

Is abstract = False

Is sealed = False

Is class = True

******************************

Field = Field2

******************************

Property = Prop

******************************

Method Name = GetHashCode

Method Return Type = System.Int32

******************************

Method Name = Equals

Method Return Type = System.Boolean

Parameter Name = obj

Parameter Type = System.Object

******************************

Method Name = ToString

Method Return Type = System.String

******************************

Method Name = Method2

Method Return Type = System.Void

Parameter Name = s

Parameter Type = System.String

Parameter Name = i

Parameter Type = System.Int32&

******************************

Method Name = get_Prop

Method Return Type = System.Int32

******************************

Method Name = set_Prop

Method Return Type = System.Void

Parameter Name = value

Parameter Type = System.Int32

******************************

Method Name = GetType

Method Return Type = System.Type

******************************

Обратите внимание, что была получена информация только об открытых членах класса Foo. Кроме этого, информация включала описание собственных и унаследованных элементов класса Foo. Пространство имен System.Reflection содержит специальное перечисление BindingFlags, которое позволяет управлять получаемой о типе информацией.

Таблица 7

Элементы перечисления BindingFlags

Элемент BindingFlags

Описание

Default

Поиск по умолчанию

IgnoreCase

Поиск, не чувствительный к регистру

DeclaredOnly

Игнорировать унаследованные члены

Instance

Поиск экземплярных членов

Static

Поиск статических членов

Public

Поиск открытых членов

NonPublic

Поиск внутренних членов

FlattenHierarchy

Поиск статических членов, заданных в базовых типах

Методы класса Type, подобные GetFields(), имеют перегруженные версии, в которых одним из параметров выступает набор флагов BindingFlags. Изменим приведенный выше код, чтобы получать информацию об открытых и внутренних методах, определенных в самом классе Foo:

BindingFlags bf = BindingFlags.DeclaredOnly |

BindingFlags.Public | BindingFlags.NonPublic |

BindingFlags.Static | BindingFlags.Instance;

MethodInfo[] mi = t.GetMethods(bf);

// Далее по тексту примера...

Некоторые типы пространства System.Reflection, которые могут быть полезны при работе с метаданными, перечисдены в таблице 8.

Таблица 8

Избранные типы пространства имен System.Reflection

Тип

Назначение

Assembly

Класс для загрузки сборки, изучения ее состава и выполнения операций со сборкой

AssemblyName

Класс для получения идентификационной информации о сборке

EventInfo

Хранит информацию о событии

FieldInfo

Хранит информацию о поле

MemberInfo

Абстрактный класс для классов вида *Info

MethodInfo

Хранит информацию о методе

Module

Позволяет обратиться к модулю в многофайловой сборке

ParameterInfo

Хранит информацию о параметре метода

PropertyInfo

Хранит информацию о свойстве

При помощи класса Assembly можно получить информацию обо всех модулях сборки, затем при помощи класса Module получить информацию обо всех типах модуля, далее вывести информацию для отдельного типа. Кроме этого, метод Assembly.Load() позволят динамически загрузить определенную сборку в память во время работы приложения. Подобный подход называется поздним связыванием.

Для демонстрации позднего связывания поместим класс Foo в динамическую библиотеку с именем FooLib.dll. Основное приложение будет загружать данную библиотеку и исследовать ее типы:

using System;

using System.Reflection;

using System.IO; //Нужно для FileNotFoundException

class MainClass {

public static void Main() {

Assembly A = null;

try {

//Используется текстовое имя без расширения

//Файл FooLib.dll находится в директории программы

A = Assembly.Load("FooLib");

} catch (FileNotFoundException e) {

Console.WriteLine(e.Message);

return;

}

foreach(Module M in A.GetModules())

foreach(Type T in M.GetTypes())

// Выводить особо нечего – одна строке Foo

Console.WriteLine(T.Name);

}

}

Позднее связывание не ограничивается загрузкой сборки и изучением состава ее элементов. При помощи позднего связывания можно создать объекты типов, определенных в сборке, а также работать с элементами созданных объектов (например, вызывать методы). Для создания определенного объекта используется метод CreateInstance() класса System.Activator. Существует несколько перегруженных версий данного метода. Можно использовать вариант, при котором в качестве параметра метода используется объект Type или строка-имя типа. Метод возвращает значение типа object. Класс MethodInfo имеет метод Invoke(), который позволяет вызвать метод объекта. Первый параметр метода Invoke() – это тот объект, у которого вызывается метод, второй параметр – массив объектов, представляющих параметры метода.

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

using System;

using System.Reflection;

using System.IO;

class MainClass {

public static void Main() {

//Просим пользователя ввести имя сборки и считываем ее

Console.Write("Enter the name of assembly: ");

string AssemblyName = Console.ReadLine();

Assembly A = Assembly.Load(AssemblyName);

//Перечисляем все типы в сборке

//(вернее, проходим по модулям сборки и ищем типы в них)

Console.WriteLine("Assembly {0} has types:", AssemblyName);

foreach(Module M in A.GetModules())

foreach(Type T in M.GetTypes())

Console.WriteLine(T.Name);

//Просим пользователя ввести имя типа

Console.Write("Enter namespace.typename: ");

string TypeName = Console.ReadLine();

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

Type UserType = A.GetType(TypeName);

object obj = Activator.CreateInstance(UserType);

//Перечисляем методы типа (с применением BindingFlags)

Console.WriteLine("Type {0} has methods:", TypeName);

BindingFlags bf = BindingFlags.DeclaredOnly |

BindingFlags.Public |

BindingFlags.NonPublic |

BindingFlags.Static |

BindingFlags.Instance;

foreach(MethodInfo m in UserType.GetMethods(bf))

Console.WriteLine("Method Name = " + m.Name);

//Просим пользователя ввести имя метода

Console.Write("Enter the name of method to call: ");

string MethodName = Console.ReadLine();

//Запрашиваем метод и выводим список его параметров

MethodInfo Method = UserType.GetMethod(MethodName);

Console.WriteLine("Method {0} has parameters:",

MethodName);

foreach(ParameterInfo pr in Method.GetParameters()) {

Console.WriteLine("Parameter Name = " + pr.Name);

Console.WriteLine("Parameter Type = " +

pr.ParameterType);

Console.WriteLine("****************************");

}

// Создаем пустой массив для фактических параметров

object[] paramArray =

new object[Method.GetParameters().Length];

//Вызываем метод

Method.Invoke(obj, paramArray);

}

}