Курс лекций для студентов специальности i-31 03 04 Информатика всех форм обучения Минск 2010



страница21/29
Дата09.08.2019
Размер0.64 Mb.
#126834
ТипКурс лекций
1   ...   17   18   19   20   21   22   23   24   ...   29

2.24. Динамические типы


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

Мотивом введения динамических типов была необходимость упростить работу C#-кода с COM-объектами и сценарными языками. Ниже приведён листинг, в котором выполняется взаимодействие с Microsoft Excel.

// получаем тип, соответствующий объекту Microsoft Excel

Type xlAppType = Type.GetTypeFromProgID("Excel.Application");


// объявляем переменную, используя динамический тип

dynamic xl = Activator.CreateInstance(xlAppType);


// вызываем некоторые методы Excel

xl.Visible = true; // показываем окно Excel

dynamic workbooks = xl.Workbooks; // создаем новый лист Excel

workbooks.Add(-4167);

xl.Cells[1, 1].Value2 = "C# Rocks!"; // заполняем ячейку листа

Пример показывает, что объект динамического типа объявляется при помощи ключевого слова dynamic. У такого объекта можно записать вызов любого метода или свойства, это не влияет на компиляцию. С точки зрения компилятора dynamic является эквивалентом object. Задача компилятора – «упаковать» информацию о действиях, производимых с динамическим объектом, чтобы среда исполнения могла правильно таким объектом распорядиться.

// объявляем простой класс

public class Foo

{

public void Do(string s) { Console.WriteLine(s); }



}
// код компилируется (!),

// но при выполнении 3-я строка генерирует исключение

dynamic obj = new Foo();

obj.Do("Hello");

obj.Prop = 3; // исключение!

Действия среды исполнения зависят от вида объекта, связываемого с динамической переменной:



  • Обычные объекты .NET – элементы типа определяются при помощи механизма отражения, работа с элементами происходит при помощи позднего связывания.

  • Объект, реализующий интерфейс IDynamicMetaObjectProvider – сам объект запрашивается о том, содержит ли он заданный элемент. В случае успеха работа с элементом делегируется объекту.

  • COM-объекты – работа с элементами происходит через интерфейс IDispatch.

Интерфейс IDynamicMetaObjectProvider позволяет разработчикам создавать типы, обладающие динамическим поведением. Обычно данный интерфейс не реализуется напрямую, а выполняется наследование от класса DynamicObject (интерфейс и класс находятся в пространстве имён System.Dynamic). Ниже приведён пример класса, унаследованного от DynamicObject.

public class MyDynamicType : DynamicObject

{

public override bool TryInvokeMember(InvokeMemberBinder binder,



object[] args, out object result)

{

Console.WriteLine("Вызов {0}.{1}()",



GetType(), binder.Name);

result = null;

return true;

}
public override bool TrySetMember(SetMemberBinder binder,

object value)

{

Console.WriteLine("Установка {0}.{1} в значение {2}",



GetType(), binder.Name, value);

return true;

}
public void DoDefaultWork()

{

Console.WriteLine("Некое действие");



}

}
// пример использования

dynamic d = new MyDynamicType();

d.DoDefaultWork(); // "Некое действие"

d.DoWork(); // "Вызов MyDynamicType.DoWork()"

d.Value = 42; // "Установка MyDynamicType.Value в значение 42"

d.Count = 12; // "Установка MyDynamicType.Count в значение 12"

Класс System.Dynamic.ExpandoObject позволяет при выполнении программы добавлять и удалять элементы своего экземпляра:

public sealed class ExpandoObject : IDynamicMetaObjectProvider,

IDictionary,

INotifyPropertyChanged

Благодаря динамическому типизированию, работа с пользовательскими элементами ExpandoObject происходит как работа с обычными элементами объекта. Ниже приведён пример расширения ExpandoObject двумя свойствами и методом (в виде делегата).

dynamic sample = new ExpandoObject();

sample.Caption = "Свойство"; // добавляем свойство Caption

sample.Number = 10; // и числовое свойство Number

sample.Increment = (Action)(() => { sample.Number++; });


// работаем с объектом sample

Console.WriteLine(sample.Caption); // Свойство

Console.WriteLine(sample.Caption.GetType()); // System.String

sample.Increment();

Console.WriteLine(sample.Number); // 11

Объект ExpandoObject явно реализует IDictionary. Это позволяет инспектировать элементы объекта при выполнении программы. Также при помощи словаря удаляются элементы объекта ExpandoObject.

dynamic employee = new ExpandoObject();

employee.Name = "John Smith";

employee.Age = 33;
foreach (var property in (IDictionary)employee)

{

Console.WriteLine(property.Key + ": " + property.Value);



}
((IDictionary)employee).Remove("Name");

Реализация в ExpandoObject интерфейса INotifyPropertyChanged позволяет получать уведомления при изменении свойств объекта ExpandoObject.


2.25. АТРИБУТЫ


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

Согласно синтаксису C#, чтобы использовать атрибут, нужно записать его имя в квадратных скобках перед тем элементом, к которому он относится. Разрешено указывать имя атрибута без суффикса Attribute, который обязателен для всех атрибутов. Можно задать в квадратных скобках несколько атрибутов через запятую. Если возникает неоднозначность трактовки цели атрибута, то нужно указать перед именем атрибута специальный префикс – assembly, module, field, event, method, param, property, return, type. Например, запись [assembly: AssemblyKeyFile] означает применение атрибута к сборке1. Любой атрибут является классом, производным от System.Attribute, а применение атрибута условно соответствует созданию объекта. Поэтому после имени атрибута указываются в круглых скобках аргументы конструктора атрибута. Если у атрибута конструктор без параметров, круглые скобки можно не писать.

При применении атрибута наряду с аргументами конструктора можно указать именованные параметры, предназначенные для задания значения открытого поля или свойства. При этом используется синтаксис имя_элемента = значение-константа. Именованные параметры всегда записываются в конце списка аргументов конструктора.

Платформа .NET предоставляет для использования обширный набор атрибутов, некоторая часть которых представлена в табл. 19.

Таблица 19

Некоторые атрибуты, применяемые в платформе .NET



Имя атрибута

Область применения

Описание

AttributeUsage

Класс

Задает область применения класса-атрибута

Conditional

Метод

Компилятор может игнорировать вызовы помеченного метода при заданном условии

DllImport

Метод

Импорт функций из DLL

MTAThread

Метод Main()

Для приложения используется модель COM Multithreaded apartment

NonSerialized

Поле

Указывает, что поле не будет сериализовано

Obsolete

Кроме param, assembly, module, return

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

ParamArray

Параметр

Позволяет одиночному параметру быть обработанным как набор параметров params

Serializable

Класс, структура, перечисление, делегат

Указывает, что все поля типа могут быть сериализованы

STAThread

Метод Main()

Для приложения используется модель COM Single-threaded apartment

StructLayout

Класс, структура

Задает схему размещения данных класса или структуры в памяти (Auto, Explicit, Sequential)

ThreadStatic

Статическое поле

В каждом потоке будет использоваться собственная копия данного статического поля

Рассмотрим единичный пример использования стандартных атрибутов. Атрибуты применяются для настройки взаимодействия программ платформы .NET и библиотек на неуправляемом коде. Атрибут [DllImport] предназначен для импортирования функций из библиотек динамической компоновки, написанных на неуправляемом коде. В следующей программе показан импорт системной функции MessageBoxA():

using System.Runtime.InteropServices;


public class MainClass

{

[DllImport("user32.dll")]



public static extern int MessageBoxA(int hWnd, string text,

string caption, uint type);


public static void Main()

{

MessageBoxA(0, "Hello World", "nativeDLL", 0);



}

}

Для использования атрибута [DllImport] требуется подключить пространство имен System.Runtime.InteropServices. Кроме этого, необходимо объявить импортируемую функцию статической и пометить её модификатором extern. Атрибут [DllImport] допускает использование дополнительных параметров, подробное описание которых можно найти в документации MSDN.



Исполняемая среда .NET выполняет корректную передачу параметров примитивных типов между управляемым и неуправляемым кодом. Для правильной передачи сложных параметров требуется использование специального атрибута [StructLayout] при объявлении пользовательского типа. Например, пусть выполняется экспорт системной функции GetLocalTime():

[DllImport("kernel32.dll")]

public static extern void GetLocalTime(SystemTime st);

В качестве параметра функция использует объект класса SystemTime. Этот класс должен быть описан следующим образом:

[StructLayout(LayoutKind.Sequential)]

public class SystemTime

{

public ushort wYear;



public ushort wMonth;

public ushort wDayOfWeek;

public ushort wDay;

public ushort wHour;

public ushort wMinute;

public ushort wSecond;

public ushort wMilliseconds;

}

Атрибут [StructLayout] указывает, что поля объекта должны быть расположены в памяти в точности так, как это записано в объявлении класса (LayoutKind.Sequential). В противном случае при работе с системной функцией вероятно возникновение ошибок.



Кроме использования готовых атрибутов возможна их самостоятельная разработка. Чтобы создать атрибут, нужно написать класс, удовлетворяющий перечисленным ниже требованиям:

    • Класс должен быть потомком класса System.Attribute.

    • Имя класса должно заканчиваться суффиксом Attribute1.

    • Тип открытых полей и свойств класса, а также параметров конструктора ограничен следующим набором: bool, byte, char, short, int, long, float, double, string; тип System.Type; перечисления; тип object; одномерные массивы перечисленных выше типов.

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

public class AuthorAttribute : Attribute

{

public string Name { get; private set; }



public string CreationDate { get; set; }
public AuthorAttribute(string name)

{

Name = name;



}

}

Далее можно применить атрибут [Author] к произвольному типу:



[Author("Developer")]

public class A { . . . }


[Author("Developer", CreationDate = "01.01.2010")]

public struct B { . . . }

При создании пользовательских атрибутов полезным оказывается использование специального класса AttributeUsageAttribute. Как видно из названия, это атрибут, который следует применить к пользовательскому классу атрибута. Конструктор класса AttributeUsageAttribute принимает единственный параметр – набор элементов перечисления AttributeTargets, определяющих область действия пользовательского атрибута (класс, метод, сборка и т. д.). Булево свойство AllowMultiple определяет, может ли атрибут быть применён к программному элементу более одного раза. Булево свойство Inherited указывает, будет ли атрибут проецироваться на потомков программного элемента.

Используем возможности класса AttributeUsageAttribute при описании пользовательского атрибута:

// атрибут Author можно применить к классу или методу несколько раз

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,

AllowMultiple = true)]

public class AuthorAttribute : Attribute { . . . }

Опишем возможности получения информации о применённых атрибутах. Метод Attribute.GetCustomAttributes() возвращает все атрибуты некоторого элемента в виде массива. Метод Attribute.GetCustomAttribute() получает атрибут заданного типа:

Attribute GetCustomAttribute(MemberInfo element, Type attributeType)

При помощи параметра element задается элемент, у которого надо получить атрибут. Второй параметр – это тип получаемого атрибута.

[Author("Developer", CreationDate = "01.01.2010")]

public class SomeClass { . . . }
// пример получения атрибута

var author = Attribute.GetCustomAttribute(typeof(SomeClass),

typeof(AuthorAttribute));

if (author != null)

{

Console.WriteLine(((AuthorAttribute) author).Name);



}

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



Каталог: images
images -> В списке студентов (или магистрантов)
images -> Н. И. Сулейманов Комплект контрольно-оценочных средств для оценки результатов освоения профессионального модуля разработан на основе Федерального государственного образовательного стандарта среднего профессионального
images -> По направлению подготовки
images -> Добавить гаджеты. Добавление гаджетов
images -> Техническое задание № apnip/C. 2/CS/Ind/01 Международный консультант по улучшенной производительности орошаемого земледелия
images -> Комплект контрольно-оценочных средств по профессиональному модулю пм. 01 Техническое обслуживание и ремонт автотранспорта


Поделитесь с Вашими друзьями:
1   ...   17   18   19   20   21   22   23   24   ...   29




База данных защищена авторским правом ©vossta.ru 2023
обратиться к администрации

    Главная страница