Создание clr dll visual studio
Начиная c MS SQL Server 2005 в распоряжение разработчиков баз данных была добавлена очень мощная технология SQL CLR.
Используя SQL CLR можно создавать написанные на высокопроизводительных языках свои хранимые процедуры, триггеры, пользовательские типы и функции, а также агрегаты. Это позволяет серьезно повысить производительность и расширить функциональность сервера до немыслимых границ.
Пользовательская функция, возвращающая таблицу
CREATE FUNCTION SplitString (@text NVARCHAR( max ), @delimiter nchar (1))
RETURNS @Tbl TABLE (part nvarchar( max ), ID_ORDER integer ) AS
BEGIN
declare @ index integer
declare @part nvarchar( max )
declare @i integer
set @ index = -1
set @i=1
while (LEN(@text) > 0) begin
set @ index = CHARINDEX(@delimiter, @text)
if (@ index = 0) AND (LEN(@text) > 0) BEGIN
set @part = @text
set @text = ''
end else if (@ index > 1) begin
set @part = LEFT (@text, @ index - 1)
set @text = RIGHT (@text, (LEN(@text) - @ index ))
end else begin
set @text = RIGHT (@text, (LEN(@text) - @ index ))
end
insert into @Tbl(part, ID_ORDER) values (@part, @i)
set @i=@i+1
end
RETURN
END
go
Эта функция разрезает входную строку используя разделитель и возвращает таблицу. Применять такую функцию очень удобно, например, для быстрого заполнения временной таблицы записями.
using System;
using System.Collections;
using System.Collections. Generic ;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
public class UserDefinedFunctions [SqlFunction(FillRowMethodName = "SplitStringFillRow" , TableDefinition = "part NVARCHAR(MAX), ID_ORDER INT" )]
static public IEnumerator SplitString(SqlString text, char [] delimiter)
if (text.IsNull) yield break ;
int valueIndex = 1;
foreach ( string s in text.Value.Split(delimiter, StringSplitOptions.RemoveEmptyEntries)) yield return new KeyValuePair< int , string >(valueIndex++, s.Trim());
>
>
static public void SplitStringFillRow( object oKeyValuePair, out SqlString value , out SqlInt32 valueIndex)
KeyValuePair < int , string >keyValuePair = (KeyValuePair< int , string >) oKeyValuePair;
valueIndex = keyValuePair.Key;
value = keyValuePair.Value;
>
>
На выходе получаем SplitString.dll
Теперь, необходимо разрешить использование CLR в SQL Server.
Все, можно подключать модуль.
Дополнительно о CLR
1. Сборка загружается на сервер и хранится там. Функции, которые ссылаются на сборку уже хранятся в базе. Поэтому нужно, чтобы на сервере, куда переносится база, сборка была загружена.
2. При создании сборки, если нужно, указывается аргумент PERMISSION_SET, который определяет разрешения для сборки. Советую посмотреть MSDN. Вкратце: SAFE — разрешает работать только с базой; EXTERNAL_ACCESS — разрешает работать с другими серверами, файловой системой и сетевыми ресурсами; UNSAFE — все что угодно, включая WinAPI.
3. Есть особенности при отладке, какие именно, указано в MSDN.
Результаты
Для сравнения скорости работы обычной SplitString и SplitStringCLR я вызвал эти функции 1000 раз с входной строкой, состоящей из 100 разделенных запятой чисел.
Среднее значение времени работы для SplitString получилось 6.152 мс, а для SplitStringCLR 1.936 мс.
В Windows библиотека динамической компоновки (DLL) является исполняемым файлом, который выступает в качестве общей библиотеки функций и ресурсов. Динамическая компоновка — это возможность операционной системы. Она позволяет исполняемому файлу вызывать функции или использовать ресурсы, хранящиеся в отдельном файле. Эти функции и ресурсы можно компилировать и развертывать отдельно от использующих их исполняемых файлов.
Библиотека DLL не является отдельным исполняемым файлом. Библиотеки DLL выполняются в контексте приложений, которые их вызывают. Операционная система загружает библиотеку DLL в область памяти приложения. Это делается либо при загрузке приложения (неявная компоновка), либо по запросу во время выполнения (явная компоновка). Библиотеки DLL также упрощают совместное использование функций и ресурсов различными исполняемыми файлами. Несколько приложений могут осуществлять одновременный доступ к содержимому одной копии библиотеки DLL в памяти.
Различия между динамической и статической компоновкой
При статической компоновке весь код объектов копируется из статической библиотеки в использующие их исполняемые файлы во время сборки. При динамической компоновке включаются только те сведения, которые позволяют Windows найти и загрузить библиотеку DLL, содержащую элемент данных или функцию, во время выполнения. При создании библиотеки DLL также создается библиотека импорта, содержащая эту информацию. При сборке исполняемого файла, который вызывает библиотеку DLL, компоновщик использует экспортированные символы в библиотеке импорта, чтобы сохранить эти сведения для загрузчика Windows. Когда загрузчик загружает библиотеку DLL, она сопоставляется с областью памяти приложения. Для выполнения операций инициализации, необходимых библиотеке DLL, вызывается специальная функция DllMain из библиотеки DLL (если она имеется).
Различия между приложениями и библиотеками DLL
Хотя и библиотеки DLL, и приложения являются исполняемыми модулями, они отличаются некоторыми особенностями. Наиболее очевидное различие заключается в том, что библиотеку DLL нельзя запустить. С точки зрения системы, между приложениями и библиотеками DLL имеется два существенных различия.
В системе может одновременно выполняться несколько экземпляров приложения. Экземпляр библиотеки DLL может быть только один.
Преимущества использования библиотек DLL
Динамическая компоновка кода и ресурсов имеет некоторые преимущества над статической.
Динамическая компоновка экономит память и сокращает подкачку. Многие процессы могут использовать библиотеку DLL совместно, одновременно обращаясь к одной доступной только для чтения копии ее частей в памяти. В отличие от этого, каждое приложение, созданное с помощью библиотеки статической компоновки, имеет полную копию кода библиотеки, которую система Windows должна загрузить в память.
Динамическая компоновка экономит место на диске и пропускную способность. Несколько приложений могут совместно использовать одну копию библиотеки DLL на диске. В отличие от этого, каждое приложение, созданное с помощью библиотеки статической компоновки, имеет код библиотеки, связанный с исполняемым образом. Это увеличивает занимаемое на диске место и используемую для передачи данных пропускную способность.
Обслуживание, применение исправлений для системы безопасности и обновление могут быть проще. Если приложения используют общие функции в библиотеке DLL, можно реализовать исправления ошибок и развернуть обновления для нее. При обновлении библиотек DLL использующие их приложения не нужно перекомпилировать или повторно компоновать. Они могут использовать новые библиотеки DLL сразу после их развертывания. В отличие от этого, при внесении исправлений в код статически скомпонованного объекта необходимо повторно скомпоновать и развернуть каждое использующее его приложение.
С помощью библиотек DLL можно оказывать послепродажную поддержку. Например, библиотеку DLL драйвера дисплея можно изменить так, чтобы она поддерживала дисплей, который не был доступен на момент предоставления приложения.
С помощью явной компоновки можно обнаруживать и загружать библиотеки DLL во время выполнения. Например, это могут быть расширения приложения, которые добавляют новые функциональные возможности без повторной сборки и развертывания приложения.
Динамическая компоновка упрощает поддержку приложений, написанных на разных языках программирования. Программы, написанные на разных языках программирования, могут вызывать одну и ту же функцию в библиотеке DLL при условии соблюдения соглашения о ее вызове. Программы и функция в библиотеке DLL должны отвечать следующим требованиям к совместимости: ожидаемый функцией порядок передачи аргументов в стек; выполнение очистки стека функцией или приложением; передача аргументов в регистрах.
Динамическая компоновка обеспечивает механизм для расширения классов библиотеки Microsoft Foundation Classes (MFC). На основе существующих классов MFC можно создавать производные классы и помещать их в библиотеку расширения DLL, используемую приложениями MFC.
Динамическая компоновка упрощает создание международных версий приложения. Библиотеки DLL — это удобный способ предоставления ресурсов для конкретных языковых стандартов, благодаря чему значительно упрощается создание международных версий приложения. Вместо предоставления множества локализованных версий приложения можно поместить строки и изображения для каждого языка в отдельную библиотеку DLL ресурсов. Затем приложение может загружать ресурсы для нужного языкового стандарта во время выполнения.
Возможным недостатком использования библиотек DLL является то, что приложения не являются автономными. Они требуют наличия отдельного модуля DLL, которое должно проверяться в процессе установки.
Дополнительные сведения о создании и использовании библиотек DLL
В приведенных ниже статьях приводятся подробные сведения о создании библиотек DLL на C и C++ в Visual Studio.
Пошаговое руководство. Создание и использование библиотеки DLL (C++)
Описывает создание и использование библиотек DLL при помощи Visual Studio.
Типы библиотек DLL
Предоставляет сведения о различных типах библиотек DLL, которые доступны для сборки.
Вопросы и ответы по библиотекам DLL
Ответы на часто задаваемые вопросы о библиотеках DLL.
Связывание исполняемого файла с библиотекой DLL
Описание явного и неявного соединения с библиотекой DLL.
Инициализация библиотеки DLL
Описывается код инициализации библиотеки DLL, который должен выполняться при загрузке библиотеки DLL.
Библиотеки DLL и поведение библиотеки времени выполнения Visual C++
Описывается последовательность запуска библиотеки DLL средой выполнения.
Функции LoadLibrary и AfxLoadLibrary
Описывается использование функций LoadLibrary и AfxLoadLibrary для явной связи с библиотекой DLL во время выполнения.
Функция GetProcAddress
Описывается использование GetProcAddress для получения адреса экспортированной функции в DLL.
Функции FreeLibrary и AfxFreeLibrary
Описывается использование функций FreeLibrary и AfxFreeLibrary , когда модуль DLL больше не нужен.
Порядок поиска библиотеки динамической компоновки (DLL)
Описание пути поиска, который операционная система Windows использует для поиска библиотеки DLL в системе.
Состояния модулей обычной библиотеки DLL MFC, динамически связанной с MFC
Описываются состояния модулей обычной библиотеки DLL, динамически связываемой с MFC.
Библиотеки DLL расширений MFC
Описываются библиотеки DLL, которые обычно реализуют классы многократного использования, производные от существующих классов MFC.
Создание библиотек DLL, содержащих только ресурсы
Библиотека DLL, содержащая только ресурсы, например значки, растровые изображения, строки и диалоговые окна.
Локализованные ресурсы в приложениях MFC: вспомогательные библиотеки DLL
Расширенная поддержка библиотек спутниковой связи DLL и содержит возможность, которая позволяет создавать приложения, локализированные на различные языки.
Импорт и экспорт
Импортирование открытых символов в приложение или экспортирование функций из библиотеки DLL
Технология Active и библиотеки DLL
Размещение серверов объектов внутри библиотеки DLL.
Автоматизация в библиотеке DLL
Параметр автоматизации в решениях мастера библиотек DLL MFC.
Соглашения об именовании библиотек DLL MFC
Способ встраивания библиотек DLL в MFC, опираясь на четко структурированное соглашение об именовании.
Связанные разделы
Использование MFC как части библиотеки DLL
Описываются постоянные библиотеки DLL MFC, которые позволяют использовать библиотеку MFC как часть библиотеки динамической компоновки Windows.
Версия библиотеки DLL MFC
Описывается механизм использования общих библиотек динамической компоновки MFCxx.dll и MFCxxD.dll (где x является номером версии MFC) с приложениями MFC и расширениями библиотек DLL MFC.
шаблон консольного приложения CLR в диалоговом окне New Project можно использовать для создания проекта консольного приложения, в котором уже есть важный набор ссылок и файлов проекта.
шаблон консольного приложения CLR можно использовать в диалоговом окне создание Project , чтобы создать проект консольного приложения, в котором уже есть ключевые ссылки и файлы проекта.
Как правило, консольное приложение компилируется в автономный исполняемый файл, но не имеет графического интерфейса пользователя. Пользователи запускают консольное приложение из командной строки. Они могут использовать командную строку для выполнения инструкций в работающем приложении. Приложение предоставляет выходные данные в виде текста в командном окне. Немедленная обратная связь консольного приложения делает его отличным средством для обучения программированию. Не нужно беспокоиться о том, как реализовать графический пользовательский интерфейс.
При создании проекта с помощью шаблона консольного приложения CLR автоматически добавляются следующие ссылки и файлы:
System, System.Data , System.Xml : Эти ссылки содержат фундаментальные классы, определяющие часто используемые типы, события, интерфейсы, атрибуты и исключения.
ConsoleApplicationName.cpp : Основной исходный файл и точка входа в приложение. Этот файл содержит базовое имя, указанное для проекта. Он определяет DLL-файл проекта и пространство имен проекта. В этом файле содержится разработанный вами код.
AssemblyInfo.cpp : Содержит атрибуты и параметры, которые можно использовать для изменения метаданных сборки проекта. Дополнительные сведения см. в разделе содержимое сборки.
stdafx.cpp : Используется для создания файла предкомпилированного заголовка с именем ConsoleApplicationName.pch и файла предкомпилированных типов с именем stdafx.obj .
stdafx.h : Используется для создания файла предкомпилированного заголовка с именем ConsoleApplicationName.pch и файла предкомпилированных типов с именем stdafx.obj .
resource.h : Созданный включаемый файл для app.rc .
app.rc : Файл описания ресурсов программы.
app.ico — Файл значка программы.
ReadMe.txt : Описывает файлы в проекте.
При создании проекта с помощью шаблона консольного приложения CLR автоматически добавляются следующие ссылки и файлы:
System, System.Data , System.Xml : Эти ссылки содержат фундаментальные классы, определяющие часто используемые типы, события, интерфейсы, атрибуты и исключения.
ConsoleApplicationName.cpp : Основной исходный файл и точка входа в приложение. Этот файл содержит базовое имя, указанное для проекта. Он определяет DLL-файл проекта и пространство имен проекта. В этом файле содержится разработанный вами код.
AssemblyInfo.cpp : Содержит атрибуты и параметры, которые можно использовать для изменения метаданных сборки проекта. Дополнительные сведения см. в разделе содержимое сборки.
pch.cpp : Используется для создания файла предкомпилированного заголовка с именем ConsoleApplicationName.pch и файла предкомпилированных типов с именем pch.obj .
pch.h : Используется для создания файла предкомпилированного заголовка с именем ConsoleApplicationName.pch и файла предкомпилированных типов с именем pch.obj .
Resource.h : Созданный включаемый файл для app.rc .
app.rc : Файл описания ресурсов программы.
app.ico — Файл значка программы.
Создание проекта консольного приложения CLR
В строке меню выберите Файл>Создать>Проект.
в диалоговом окне создание Project выберите установленные>шаблоны>Visual C++> узелclr , а затем выберите шаблон консольное приложение clr .
В поле Имя введите уникальное имя для приложения.
Можно указать другие параметры проекта и решения, но они не являются обязательными.
В строке меню выберите Файл>Создать>Проект.
в диалоговом окне создание Project выберите установленный>Visual C++> узелclr , а затем выберите шаблон консольное приложение clr .
В поле Имя введите уникальное имя для приложения.
Можно указать другие параметры проекта и решения, но они не являются обязательными.
В строке меню выберите Файл>Создать>Проект.
В поле Имя введите уникальное имя для приложения.
Можно указать другие параметры проекта и решения, но они не являются обязательными.
Одним из наиболее важных новшеств в готовящейся к выпуску версии SQL Server 2005 (ранее — проект Yukon) является возможность исполнения кода на основе среды исполнения Common Language Runtime (CLR). Разработчики программного обеспечения и администраторы баз данных (DBA) наверняка уже слышали о CLR. Но администраторы, которые никак не связаны с разработкой для SQL Server, могли и не обращать внимания на CLR, полагая, что на их работу CLR никак не повлияет. На мой взгляд, это не так: поскольку роль каждого, кто работает с SQL Server, постепенно меняется, необходимо иметь представление об этой новой технологии, и неважно, кем вы сами себя считаете — DBA, разработчиком или программистом.
В SQL Server 2000 и более ранних версиях SQL можно было обращаться к внешнему коду из расширенной хранимой процедуры (extended stored procedure), которая вызывала предварительно скомпилированную библиотеку DLL, созданную с учетом спецификации Open Data Services (ODS) API. Расширенные процедуры использовались для выполнения сложных математических вычислений, с привлечением функциональности операционной системы, ресурсов процессора или просто для получения функциональности, отсутствовавшей в T-SQL. Проблема при работе с расширенными процедурами состояла в том, что SQL Server должен был полностью «доверять» внешнему коду. Поскольку при создании расширенных хранимых процедур использовался неуправляемый код, а процедуры работали в адресном пространстве SQL Server, проблемы, возникающие в расширенных процедурах, могли нарушить работу SQL Server — фактически дело могло дойти до «голубого экрана смерти». Исполняемый код, созданный на базе CLR, означает, что разработчики SQL Server и DBA получают возможность безопасного наращивания возможностей своих процедур.
Знакомство с CLR
Рис. 2 иллюстрирует принцип работы CLR. Visual Studio запускает CLR-компилятор, который получает блок кода, называемый сборкой (assembly), и генерирует динамическую библиотеку, DLL. В ней содержится код в форме промежуточного языка (intermediate language, IL). CLR конвертирует этот код в машинный код и выполняет его.
Будем считать, что вызов DLL из T-SQL подобен вызову внешнего (по отношению к основному процессу) COM-объекта. Проверенный принцип программирования гласит: пореже обращайтесь к внешним процедурам, передавайте им большее число параметров и стремитесь к тому, чтобы на долю продуктивного времени в процедурах отводилось максимально возможное время. Говоря о продуктивном времени работы приложения во внешнем коде, я подразумеваю время продуктивного и эффективного использования циклов процессора.
Когда внешняя библиотека, CLR DLL, вызывается в первый раз, SQL Server должен загрузить код в память, скомпилировать исполняемый код и затем выполнить его. После того как сервер SQL выполнит эти действия, вызов внешнего объекта потребует меньших затрат по сравнению с первоначальным вызовом, но все же окажется более «дорогим», нежели вызов внутрипроцессного кода. Результат? Вызов CLR-объекта может оказаться более медленным, чем вызов хранимой процедуры T-SQL.
На уже упоминавшейся встрече директоров и редакторов рассматривались некоторые ситуации, когда сборки CLR для SQL Server могут оказаться жизнеспособными альтернативами процедурам T-SQL. Разработчики SQL Server уже реализовали решения фактически для всех подобных случаев, используя расширенные хранимые процедуры и библиотеки COM DLL. Однако в каждом из перечисленных ниже случаев было бы целесообразно переписать существующие решения на CLR-код.
- Замена или наращивание функциональности сложных хранимых процедур T-SQL, особенно тех из них, в которых выполняются математические вычисления с использованием, например, сложных бизнес- или научных формул. Математические возможности T-SQL не намного лучше моих собственных, поэтому перенос сложной математики в CLR DLL может оказаться целесообразным.
- Обращение к файлу Microsoft Project для обновления сложных задач, связанных с планированием. Логика CLR-процедур позволяет приложениям взаимодействовать со структурами данных Project, к которым T-SQL не может обратиться напрямую. Аналогичный подход может применяться для любых внешних структур данных, которые не имеют традиционного интерфейса доступа к данным через процессы или файлы.
- Реализация сложных алгоритмов картографии. Используя координаты Geographic Positioning Satellite (GPS), один из региональных директоров составил запрос: "Показать все аэропорты в пяти милях от города". С помощью CLR-процедуры, работающей с геометрическими функциями, решить поставленную задачу оказалось гораздо проще, чем если бы пришлось писать сложный запрос в базу данных картографии.
- Выполнение сложных инженерных вычислений. К примеру, моя дочь купила справочник толщиной 15 см с формулами химического машиностроения. С помощью CLR я мог бы закодировать эти формулы и с легкостью обращаться к ним из T-SQL как к функциям. Но напоминаю, что следует быть внимательным при оценке времени, необходимого для вычисления формул при стандартном подходе и при переходе на CLR.
В каждом из этих примеров CLR-код расширяет возможности и сферу применения языка запросов T-SQL. Именно для этого и создаются процедуры, основанные на использовании CLR.
Тестирование
Конечно, элементарные преобразования — это не то, ради чего создаются хранимые процедуры на CLR; они предназначены для сложных и ресурсоемких вычислений либо для тех случаев, когда решить задачу средствами традиционного T-SQL невозможно. Хранимые процедуры на CLR задумывались как замена расширенных хранимых процедур, а не как их расширение.
После консультаций с Петером Блекберном (Peter Blackburn), моим техническим гуру из Великобритании, я решил попробовать немного усложнить тест — сделать его более реалистичным. На этот раз речь шла о реализации функции RSA-шифрования с привлечением провайдера RSACryptoServiceProvider, на вход которому подается строка и возвращается зашифрованный байтовый массив. Почти три недели спустя новое приложение было готово к тестированию. На рис. 3 представлена схема его работы. В ходе разработки я обнаружил несколько проблем, из-за которых мне пришлось создать большую часть проекта в пакетах T-SQL. Кроме того, обнаружилась новая возможность отладки (ее описание приведено во врезке «Отладка процедур T-SQL и SQL CLR»). Приобретенный опыт позволил поближе познакомиться с новым инструментарием SQL Server 2005 — SQL Server Management Studio (ранее — проект Workbench), который заменил Enterprise Manager, Query Analyzer и еще несколько утилит. Microsoft в настоящее время интегрирует инструментарий Management Studio в Visual Studio 2005, чтобы упростить обслуживание баз данных SQL Server.
Применявшийся мною процесс создания приложения по сравнению со способом проектирования приложений, используемым многими разработчиками, шел в обратном направлении.
Как показано на экране 1, программа получает номер и срок действия кредитной карты пользователя. Когда пользователь нажимает кнопку Submit, из основного кода происходит вызов хранимой процедуры, которая в свою очередь вызывает сборку CLR. Последняя генерирует секретный ключ шифрования RSA и использует этот ключ для шифрования номера кредитной карты пользователя. Хранимая процедура записывает срок действия кредитной карты и шифрованные данные в базу данных (на этот раз в виде байтового массива). Основной код сохраняет секретный ключ шифрования (private encryption key, PEK) и отображает его в клиентском приложении для облегчения процедуры отладки. Однако PEK на сервере не хранится; таким образом, только сам пользователь может дешифровать закодированное значение на сервере, и только в том случае, если клиентское приложение хранит это значение на локальном диске. Я не могу сравнивать приведенный пример программ шифрования/дешифрации с эквивалентным кодом на T-SQL, такая функциональность в T-SQL просто не поддерживается, но этот пример демонстрирует механику создания CLR-кода, и описанный неформальный тест показал, что он прекрасно работает. Однако я бы посоветовал поэкспериментировать со своим CLR-кодом в условиях, приближенных к конкретной бизнес-среде.
Когда пользователю нужно извлечь номер своей кредитной карты, чтобы серверная функция смогла использовать его, приложение на стороне клиента вызывает соответствующую хранимую процедуру. Процедура извлекает ключ шифрования, находит запись клиента по его ID и сроку действия и дешифрует значение при помощи другой процедуры на CLR. Для облегчения отладки приложения код возвращает расшифрованный номер карты в клиентское приложение (в «боевой» программе серверное приложение может, например, просто разблокировать полученное значение для выполнения дальнейшей обработки).
Нужно иметь в виду, что приложение вызывает хранимые процедуры на CLR из процедур T-SQL. Я нахожу такой подход более удобным, чем непосредственный вызов CLR-процедур, и полагаю, что большинство разработчиков придут к такому же выводу. Другой подход состоит в кодировании CLR-процедур в виде функций и встраивании их в код T-SQL.
Итак, мы рассмотрели некоторые концепции хранимых CLR-процедур. В статье было показано, как работают такие процедуры и где они в большей мере соответствуют высокопроизводительным системам управления базами данных (DBMS). В дальнейшем я собираюсь рассказать о том, как применять Visual Studio 2005 и SQL Server 2005 для кодирования и тестирования хранимых CLR-процедур, используемых в тестовом приложении, которое было представлено выше.
Отладка процедур T-SQL и SQL CLR
В качестве примера приведу текст программы, выводящий на экран возраст объекта:
исходный текст программы, чтобы было понятно:
using System;
namespace ConsoleApplication_Test_Csharp
public class SomeClass
int age;
public int GetAge()
age = 22;
return age;
>
>
public sealed class Program
<
public static void Main()
System. Console .Write( "My age is " );
SomeClass me = new SomeClass();
int myAge;
myAge = me.GetAge();
System. Console .WriteLine(myAge);
Console .ReadLine();
>
>
>
* This source code was highlighted with Source Code Highlighter .
И так приступим:
CLR (Common language runtime) — общеязыковая исполняющая среда. Она обеспечивает интеграцию языков и позволяет объектам благодаря стандартному набору типов и метаданным), созданным на одном языке, быть «равноправными гражданами» кода, написанного на другом.
Компилятор, помимо ассемблера IL создает полные метаданные.
Метаданные — набор из таблиц данных, описывающих то, что определено в модуле. Также есть таблицы, указывающие на что ссылается управляемый модуль (например, импортируемые типы и числа). Они расширяют возможности таких технологий как библиотеки типов и файлы языка описания интерфейсов (IDL). Метаданные всегда связаны с файлом с IL кодом, фактически они встроены в *.exe или *.dll.
Таким образом метаданные это таблицы, в которых есть поля, говорящие о том, что такой-то метод находится в таком-то файле и принадлежит такому-то типу(классу).
Вот как выглядят метаданные для моего примера (таблицы метаданных просто преобразованы в понятный вид с помощью дизассемблера ILdasm.exe. На самом деле это часть *.exe файла программы:
Разобравшись с основными понятиями, давайте посмотрим из чего же состоит тот самый управляемый модуль (или просто наш файл ConsoleApplication_Test_Csharp.exe, который выполняет вывод на экран возраста объекта):
И так, что же происходит, когда запускается впервые программа?
Сперва происходит анализ заголовка, чтобы узнать какой процесс запустить (32 или 64 разрядный). Затем загружается выбранная версия файла MSCorEE.dll ( C:\Windows\System32\MSCorEE.dll для 32разрядных процессоров)
После чего вызывается метод, расположенный MSCorEE.dll, который и инициализирует CLR, сборки и точку входа функции Main() нашей программы.
static void Main()
System. Console .WriteLine( "Hello " );
System. Console .WriteLine( "Goodbye" );
>
* This source code was highlighted with Source Code Highlighter .
Для выполнения какого-либо метода, например System.Console.WriteLine(«Hello „), IL должен быть преобразован в машинные команды (те самые нули и единицы) Этим занимается Jiter или just-in-time compiler.
Сперва, перед выполнением Main() среда CLR находит все объявленные типы (например тип Console).
Затем определяет методы, объединяя их в записи внутри единой “структуры» (по одному методу определенному в типе Console).
Записи содержат адреса, по которым можно найти реализации методов (т.е. те преобразования, которые выполняет метод).
При первом обращение к функции WriteLine вызывается JiT-compiler.
JiTer 'у известны вызываемый метод и тип, которым определен этот метод.
JiTer ищет в метаданных соответствующей сборки — реализацию кода метода (код реализации метода WriteLine(string str) ).
Затем, он проверяет и компилирует IL в машинный код (собственные команды), сохраняя его в динамической памяти.
После JIT Compiler возвращается к внутренней «структуре» данных типа (Console) и заменяет адрес вызываемого метода, на адрес блока памяти с исполняемыми процессорными командами.
После этого метод Main() обращается к методу WriteLine(string str) повторно. Т.к. код уже скомпилирован, обращение производится минуя JiT Compiler. Выполнив метод WriteLine(string str) управление возвращается методу Main().
Из описания следует, что «медленно» работает функция только в момент первого вызова, когда JIT переводит IL код в инструкции процессора. Во всех остальных случаях код уже находится в памяти и подставляется как оптимизированный для данного процессора. Однако если будет запущена еще одна программа в другом процессе, то Jiter будет вызван снова для того же метода. Для приложений выполняемых в х86 среде JIT генерируется 32-разрядные инструкции, в х64 или IA64 средах — соответственно 64-разрядные.
IL может быть оптимизирован, т.е. из него будут удалены IL — команды NOP (пустая команда). Для этого при компиляции нужно добавить параметры
Debug версия собирается с параметрами: /optimize -, /debug: full
Release версия собирается с параметрами: /optimize +, /debug: pdbonly
Чем же отличается управляемый код от неуправляемого?
Неуправляемый код компилируется для конкретного процессора и при вызове просто исполняется.
В управляемой среде компиляция производится в 2 этапа:
Взаимодействие с неуправляемым кодом:
— управляемый код может вызывать направляемую функцию из DLL посредствам P/Invoke (например CreateSemaphore из Kernel32.dll).
— управляемый код может использовать существующий COM-компонент (сервер).
— неуправляемый код может использовать управляемый тип (сервер). Можно реализовать COM — компоненты в управляемой среде и тогда не нужно вести подсчет ссылок интерфейсов.
Параметр /clr позволяет скомпилировать Visual С++ код в управляемые IL методы (кроме когда, содержащего команды с ассемблерными вставками ( __asm ), переменное число аргументов или встроенные процедуры ( __enable, _RetrurAddress )). Если этого сделать не получится, то код скомпилируется в стандартные х86 команды. Данные в случае IL кода не являются управляемыми (метаданные не создаются) и не отслеживаются сборщиком мусора (это касается С++ кода).
В дополнение хочу рассказать о системе типов CTS, принятой Microsoft.
— CTS поддерживает только единичное наследование (в отличие от С++)
— Все типы наследуются от System.Object (Object — имя типа, корень все остальных типов, System — пространство имен)
По спецификации CTS любой тип содержит 0 или более членов.
Поле — переменная, часть состояния объекта. Идентифицируются по имени и типу.
Метод — функция, выполняющая действие над объектом. Имеет имя, сигнатуру(число параметров, последовательность, типы параметров, возвр. значение функции) и модификаторы.
Свойство — в реализации выглядит как метод (get/set) а для вызывающей стороны как поле ( = ). Свойства позволяют типу, в котором они реализованы, проверить входные параметры и состояние объекта.
Событие — обеспечивает механизм взаимного уведомления объектов.
Public — метод доступен любому коду из любой сборки
Private — методы вызывается только внутри типа
Family (protected) — метод вызывается производными типами независимо от сборки
Assembly (internal) — метод вызывается любым кодом из той же сборки
Family or Assembly
(protected internal) — метод вызывается производными типами из любой сборки и + любыми типами из той же сборки.
Пример проверки на соответствие CLS
Атрибут [assembly: CLSCompliant(true)] заставляет компилятор обнаруживать любые доступные извне типы, содержащие конструкции, недопустимые в других языках.
- using System;
- [assembly: CLSCompliant( true )]
- namespace SomeLibrary
- // возникает предупреждение поскольку тип открытый
- public sealed class SomeLibraryType
- // тип, возвращаемый функцией не соответсвует CLS
- public UInt32 Abc()
- // идентификатор abc() отличается от предыдущего, только если
- // не выдерживается соответсвие
- public void abc()
- // ошибки нет, метод закрытый
- private UInt32 ABC()
- >
- >
Первое предупреждение: UInt32 Abc() возвращает целочисленное целое без знака. Visaul Basic, например, не работает с такими значениями.
Второе предупрждение: два открытых метода Abc() и abc() — одиноквые и отличаются лишь регистром букв и возвращаемым типом. VisualBasic не может вызывать оба метода.
Убрав public и оставив только sealed class SomeLibraryType оба предупреждения исчезнут. Так как SomeLibraryType по-умолчанию будет internal и не будет виден извне сборки.
Читайте также: