Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OOP_answers (1).docx
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
2.9 Mб
Скачать

Исследование объекта

Итак, предположим, мы изучили класс досконально. Теперь мы знаем о нём всё. Как же воспользоваться этим знанием, если имеется готовый экземпляр класса? Как прочитать его поля, как получить значения его свойств, как вызывать его методы? Давайте для начала научимся читать поля и свойства объекта.

ПРИМЕЧАНИЕ

- Эка невидаль, - скажете вы мне. - Прочитать поля и свойства объекта! Что ж тут такого?

- Дело в том, что класс, экземпляр которого мы хотим создать и исследовать, не известен в момент написания программы. Более того, он не только не известен, он даже ещё и не написан! Его напишут после того, как программа будет скомпилирована!

Предположим, у вас имеется ссылка на объект неизвестного типа. В .Net это означает, что имеется ссылка на объект типа Object. Тип Object – это базовый тип CLR, и к нему можно привести любую ссылку (value-типы перед этим должны быть помещены в GC-хип, т.е. должна быть произведена операция boxing'а). У Object есть только базовый набор методов, через которые нельзя напрямую вызвать методы типа, приведенного к Object. Но с помощью Reflection можно динамически получить описание методов, полей или свойств объекта. А с помощью этого описания можно динамически к ним обращаться. Следующий пример считывает список полей и выводит их имена и значения:

public static void TraceObjectFields(Object obj)

{

foreach (FieldInfo mi in obj.GetType().GetFields())

Console.WriteLine(mi.FieldType.Name

+ " " + mi.Name + "= " + mi.GetValue(obj);

}

А этот выводит список свойств объекта:

public static void TraceObjectProperties(Object obj)

{

foreach (PropertyInfo pi in obj.GetType().GetProperties())

Console.WriteLine(pi.PropertyType.Name

+ " " + pi.Name + " = " + pi.GetValue(obj, null);

}

Динамическое создание объекта и вызов методов

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

Создание объекта по его типу

При создании объекта вызывается его конструктор. Если вас устраивает вариант создания объекта с вызовом конструктора без аргументов (конструктора по умолчанию), то задача решается очень просто. Например, вот так:

class Class1

{

int n = 5;

static void Main()

{

string className = "App1.Class1";

Type type = Type.GetType(className);

Object data = Activator.CreateInstance(type);

Console.WriteLine(Trace1.ObjectFields("data", data));

Console.ReadLine();

}

}

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

ПРИМЕЧАНИЕ

Наряду с классом Activator способностью создавать объекты по их типу обладают классы Assembly, AppDomain и некоторые другие. У них тоже есть методы CreateInstance, работа с которыми ведётся подобным образом.

Хочу обратить ваше внимание на то, как определялось, что тип, считанный из сборки, реализует необходимый интерфейс. В данном случае не было экземпляра объекта, к которому применялась бы операция as. Поэтому был использован метод IsAssignableFrom класса Type.

Говоря точнее, в большинстве случаев у нас есть возможность создать экземпляр объекта, а потом применить к нему as, но делать так не совсем рационально. Во-первых, создание объекта может быть недешёвым удовольствием, тем более что неизвестно, будем ли мы его использовать в дальнейшем, или он нам не подойдёт. Во-вторых, возможно, что объект всё равно не удастся создать, потому что тип может быть абстрактным классом или интерфейсом.

Класс Type имеет ещё один метод со схожей функциональностью – IsInstanceOfType. В отличие от IsAssignableFrom он принимает в качестве аргумента не объект Type, а экземпляр объекта. Метод IsInstanceOfType определяет, является ли тип объекта, переданного в аргументе, наследником типа, для которого этот метод вызван. Иначе можно это сформулировать вот так:

type.IsInstanceOfType(obj) = obj.GetType().IsAssignableFrom(type)

Если речь зашла о приведениях типов и о выяснении того, кто чей предок, то нельзя не коснуться операций is и as. Честно говоря, я начал немного путаться в этих функциях и операциях, и сделал себе такую табличку. Может, она и вам пригодится?

Возвращаемое значение

Применяется к:

Оператор или функция

Тип аргумента

Комментарий

bool

Object

is

тип

Оператор is позволяет определить, является ли объект (Object) наследником некоторого класса или интерфейса. Оператор также применим к массивам, и позволяет узнать, является ли объект массивом, совместимым с заданным типом.

Object

Object

as

тип

Оператор as пытается привести ссылку на объект или интерфейс (Object) к некоторому типу (возможно, интерфейсу). Если это невозможно (в смысле is), возвращается null.

bool

Type

IsInstanceOfType

Object

Позволяет определить, является ли некоторый экземпляр объекта (Object) экземпляром указанного типа (Type).

bool

Type

IsSubclassOf

Type

Позволяет определить, что тип является наследником типа, переданного в качестве аргумента, и не является абстрактным. Если типы одинаковые, возвращается false.

bool

Type

IsAssignableFrom

Type

Позволяет определить, что в переменную данного типа можно установить значение, тип которого указан в качестве параметра метода IsAssignableFrom. Также говорит о возможности приведения типов.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]