- •А.А. Волосевич
- •2. Базовые технологии платформы .Net 5
- •2. Базовые технологии платформы .Net 4
- •2. Базовые технологии платформы .Net
- •2.1. Работа с Числами
- •2.2. Дата и время
- •2.3. Работа со строками и текстом
- •2.4. Преобразование информации
- •2.5. Отношения равенства и порядка
- •Сравнение для выяснения равенства
- •Сравнение для выяснения порядка
- •2.6. Жизненный цикл объектов
- •Алгоритм «сборки мусора»
- •Финализаторы и интерфейс iDisposable
- •2.7. Перечислители и итераторы
- •2.8. Интерфейсы стандартных коллекций
- •2.9. Массивы и класс system.Array
- •2.10. Типы для работы с коллекциями-списками
- •2.11. Типы для работы с коллекциями-множествами
- •2.12. Типы для работы с коллекциями-словарями
- •2.13. Типы для создания пользовательских коллекций
- •2.14. Технология linq to objects
- •1. Оператор условия Where().
- •2. Операторы проекций.
- •3. Операторы упорядочивания.
- •4. Оператор группировки GroupBy().
- •5. Операторы соединения.
- •6. Операторы работы с множествами.
- •7. Операторы агрегирования.
- •8. Операторы генерирования.
- •9. Операторы кванторов и сравнения.
- •10. Операторы разбиения.
- •11. Операторы элемента.
- •12. Операторы преобразования.
- •2.15. Работа с объектами файЛовой системы
- •2.16. Ввод и вывод информации
- •Потоки данных и декораторы потоков
- •2. Классы для работы с потоками, связанными с хранилищами.
- •3. Декораторы потоков.
- •4. Адаптеры потоков.
- •Адаптеры потоков
- •2.17. Основы xml
- •2.18. Технология linq to xml
- •Создание, сохранение, загрузка xml
- •Запросы, модификация и трансформация xml
- •Пространства имен xml
- •2.19. ДОполнительные возможности обработки xml
- •2.20. Сериализация
- •Сериализация времени выполнения
- •Сериализация контрактов данных
- •2.21. Состав и взаимодействие сборок
- •2.22. Метаданные и получение информации о типах
- •2.23. Позднее связывание и кодогенерация
- •2.24. Динамические типы
- •2.25. Атрибуты
- •2.26. Файлы конфигуРации
- •2.27. Основы мНогопоточноГо программирования
- •2.28. Синхронизация потоков
- •2.29. Библиотека параллельных расширений
- •Параллелизм на уровне задач
- •Параллелизм при императивной обработке данных
- •Параллелизм при декларативной обработке данных
- •Обработка исключений и отмена выполнения задач
- •Коллекции, поддерживающие параллелизм
- •2.30. Асинхронный вызов методов
- •2.31. Процессы и домены
- •2.32. Безопасность
- •Разрешения на доступ
- •Изолированные хранилища
- •Криптография
- •2.33. Диагностика
2.23. Позднее связывание и кодогенерация
Механизм отражения позволяет реализовать на платформе .NET позднее связывание (late binding). Этот термин обозначает процесс динамической загрузки типов при работе приложения, создание экземпляров типов и работу с элементами экземпляров.
Продемонстрируем позднее связывание на примере класса SimpleClass, размещённого в локальной сборке SimpleLibrary.dll.
namespace SimpleLibrary
{
public class SimpleClass
{
public SimpleClass() { }
public SimpleClass(int offset) { Offset = offset; }
public int Offset { get; set; }
public int Sum(int x) { return x + Offset; }
}
}
Первый этап позднего связывания – загрузка в память сборки с типом – выполняется при помощи метода Assembly.Load(). Для указания имени сборки можно использовать простую строку или объект класса AssemblyName.
AssemblyName assemblyName = new AssemblyName("SimpleLibrary");
// возможные исключения не обрабатываются!
Assembly assembly = Assembly.Load(assemblyName);
После загрузки сборки нужно создать объект требуемого типа. Для этого следует воспользоваться экземплярным методом Assembly.CreateInstance()1.
var typeName = "SimpleLibrary.SimpleClass";
// короткая версия
var o1 = (SimpleLibrary.SimpleClass)assembly.CreateInstance(typeName);
// полная версия, можно задать параметры конструктора (5-й аргумент)
var o2 = (SimpleLibrary.SimpleClass)assembly.CreateInstance(typeName,
false, BindingFlags.Default, null, new object[] {10},
CultureInfo.InvariantCulture, null);
В листинге, приведённом ниже, имя сборки, имя типа и метод объекта запрашиваются у пользователя в процессе выполнения программы.
Console.Write("Input assembly name: ");
Assembly assembly = Assembly.Load(Console.ReadLine());
Console.Write("Input full typename: ");
Type type = assembly.GetType(Console.ReadLine());
object obj = Activator.CreateInstance(type);
Console.Write("Input method name: ");
MethodInfo method = type.GetMethod(Console.ReadLine());
// создаём пустой массив для фактических параметров
var paramArray = new object[method.GetParameters().Length];
// вызываем метод, указывая целевой объект и набор аргументов
method.Invoke(obj, paramArray);
Платформа .NET имеет несколько средств, позволяющих выполнять различные виды кодогенерации, в частности, генерацию инструкций MSIL, генерацию деревьев исходного кода, обработку деревьев выражений.
Средства генерации инструкций промежуточного языка MSIL сосредоточены в пространстве имён System.Reflection.Emit. Данные средства востребованы, в основном, разработчиками компиляторов для платформы .NET.
Пространство имён System.CodeDom содержит типы для описания структуры документа с исходным кодом программы. Чтобы получить по такому документу листинг на выбранном языке программирования и скомпилировать его, нужны типы из пространства имён System.CodeDom.Compiler.
var compileUnit = new CodeCompileUnit(); // единица компиляции
// опишем пространство имён и свяжем его с единицей компиляции
var namespase = new CodeNamespace("SimpleLibrary");
namespase.Imports.Add(new CodeNamespaceImport("System"));
compileUnit.Namespaces.Add(namespase);
// опишем очень простой класс
var type = new CodeTypeDeclaration {
Name = "SimpleClass",
IsClass = true,
TypeAttributes = TypeAttributes.Public};
namespase.Types.Add(type);
// создадим элемент класса (поле Offset)
var field = new CodeMemberField {
Name = "Offset",
Type = new CodeTypeReference("System.Int32"),
Attributes = MemberAttributes.Public};
type.Members.Add(field);
// создадим исходник на языке C# и запишем его в файл
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
using (var stream = new StreamWriter("SimpleClass.cs"))
{
provider.GenerateCodeFromCompileUnit(compileUnit, stream, null);
}
Деревья выражений описывают код в виде данных, которые хранятся в древовидной структуре. Каждый узел в дереве представляет выражение, например, вызов метода или бинарную операцию. Для хранения деревьев выражений служит класс System.Linq.Expressions.Expression<T>.
Простейший способ создать дерево выражений в C# – использовать возможности компилятора, генерирующего деревья для лямбда-выражений:
Func<int, bool> lambda = n => n < 5; // обычная лямбда
Expression<Func<int, bool>> tree = n => n < 5; // дерево
Пространство имен System.Linq.Expressions предоставляет программный интерфейс для создания деревьев выражений. Класс Expression содержит статические методы, которые создают узлы дерева выражений особых типов. Например, выражение ParameterExpression представляет именованный параметр, а выражение LoopExpression описывает цикл.
// построим вручную дерево выражений для лямбды n => n < 5
ParameterExpression n = Expression.Parameter(typeof(int), "n");
ConstantExpression five = Expression.Constant(5, typeof(int));
BinaryExpression nLessFive = Expression.LessThan(n, five);
Expression<Func<int, bool>> tree =
Expression.Lambda<Func<int, bool>>(nLessFive, new[] {n});
У созданного дерева выражений можно исследовать структуру, изменять элементы и компилировать его в инструкции MSIL:
Expression<Func<int, bool>> tree = n => n < 5;
// декомпозиция дерева выражений
var param = tree.Parameters[0];
var op = (BinaryExpression) tree.Body;
var left = (ParameterExpression) op.Left;
var right = (ConstantExpression) op.Right;
Console.WriteLine("{0} => {1} {2} {3}",
param.Name, left.Name, op.NodeType, right.Value);
// компиляция дерева и вызов лямбды
Func<int, bool> lambda = tree.Compile();
Console.WriteLine(lambda(10));