- •А.А. Волосевич
- •1. Работа с Числами
- •2. Дата и время
- •3. Работа со строками и текстом
- •4. Преобразование информации
- •5. Отношения равенства и порядка
- •Сравнение для выяснения равенства
- •Сравнение для выяснения порядка
- •6. Жизненный цикл объектов
- •Алгоритм «сборки мусора»
- •Финализаторы и интерфейс iDisposable
- •7. Перечислители и итераторы
- •8. Интерфейсы стандартных коллекций
- •9. Массивы и класс system.Array
- •10. Типы для работы с коллекциями-списками
- •11. Типы для работы с коллекциями-множествами
- •12. Типы для работы с коллекциями-словарями
- •13. Типы для создания пользовательских коллекций
- •14. Технология linq to objects
- •1. Оператор условия Where().
- •2. Операторы проекций.
- •3. Операторы упорядочивания.
- •4. Оператор группировки GroupBy().
- •5. Операторы соединения.
- •6. Операторы работы с множествами.
- •7. Операторы агрегирования.
- •8. Операторы генерирования.
- •9. Операторы кванторов и сравнения.
- •10. Операторы разбиения.
- •11. Операторы элемента.
- •12. Операторы преобразования.
- •15. Работа с объектами файЛовой системы
- •16. Ввод и вывод информации
- •Потоки данных и декораторы потоков
- •2. Классы для работы с потоками, связанными с хранилищами.
- •3. Декораторы потоков.
- •4. Адаптеры потоков.
- •Адаптеры потоков
- •17. Основы xml
- •18. Технология linq to xml
- •Создание, сохранение, загрузка xml
- •Запросы, модификация и трансформация xml
- •Пространства имён xml
- •19. ДОполнительные возможности обработки xml
- •20. Сериализация
- •Сериализация времени выполнения
- •Сериализация контрактов данных
- •21. Состав и взаимодействие сборок
- •22. Метаданные и получение информации о типах
- •23. Позднее связывание и кодогенерация
- •24. Динамические типы
- •25. Атрибуты
- •26. Файлы конфигуРации
- •27. Основы мНогопоточноГо программирования
- •28. Синхронизация потоков
- •29. Библиотека параллельных расширений
- •Параллелизм на уровне задач
- •Параллелизм при императивной обработке данных
- •Параллелизм при декларативной обработке данных
- •Обработка исключений и отмена выполнения задач
- •Коллекции, поддерживающие параллелизм
- •30. Асинхронный вызов методов
- •31. Процессы и домены
- •32. Безопасность
- •Разрешения на доступ
- •Изолированные хранилища
- •Криптография
- •33. Диагностика
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 имеет несколько средств, позволяющих выполнять различные виды кодогенерации, в частности, генерацию инструкций CIL, генерацию деревьев исходного кода, обработку деревьев выражений.
Средства генерации инструкций промежуточного языка CIL сосредоточены в пространстве имён 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});
У созданного дерева выражений можно исследовать структуру, изменять элементы и компилировать его в инструкции CIL:
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));