Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции OOP c#.doc
Скачиваний:
44
Добавлен:
22.09.2019
Размер:
3.38 Mб
Скачать

3. ТЕхнология .Net Remoting

3.1. Домены приложений

Любому запущенному приложению в операционной системе соответствует некий процесс. Процесс образует границы приложения, и для взаимодействия процессов требуется применять специальные средства. В .NET Framework процессы дополнительно подразделяются на домены приложений. Один домен может содержать несколько сборок. Различным доменам могут соответствовать различные политики безопасности, домены могут создаваться и уничтожаться в ходе работы в рамках одного приложения.

Рассмотрим работу с доменами на примерах. Пусть имеется следующая сборка, размещенная в динамической библиотеке Students.dll:

using System;

public class Student {

public string Name;

public int Age;

public double MeanScore;

public Student(string Name, int Age, double MeanScore) {

this.Name = Name;

this.Age = Age;

this.MeanScore = MeanScore;

log.print("New Student object created");

}

public void sayName() {

log.print("sayName() is called");

Console.WriteLine("My name is {0}", Name);

}

}

public class log {

public static void print(string s) {

Console.WriteLine("[{0}]: {1}",

AppDomain.CurrentDomain.FriendlyName,s);

}

}

Домены приложений инкапсулированы в объектах класса System.AppDomain. Любое приложение при запуске создает домен по умолчанию, в который загружается главная сборка приложения. Для создания нового домена используется статический метод AppDomain.CreateDomain(). Существует несколько перегруженных версий этого метода, которые обеспечивают применение к создаваемому домену определенных политик безопасности. В простейшем случае методу передается в качестве параметра только строка с дружественным именем домена, которое используется как идентификатор домена:

AppDomain ad2 = AppDomain.CreateDomain("New Domain");

После создания домена в него можно загрузить сборки, используя метод Load()1 (указывается слабое или сильное имя сборки):

ad2.Load("Students");

Отдельную сборку выгрузить из домена нельзя, но можно выгрузить домен целиком, используя его метод Unload():

AppDomain.Unload(ad2);

В любом домене можно создавать объекты различных типов. Данные действия выполняются при помощи методов CreateInstance(), CreateInstanceFrom(), CreateInstanceAndUnwrap(), CreateInstanceFromAndUnwrap(). Рассмотрим подробнее метод CreateInstance(). Существует несколько перегруженных версий этого метода. В простейшем случае в качестве параметров метода указывается имя сборки и полное имя типа. Самый полный вариант CreateInstance() имеет следующую сигнатуру:

public virtual ObjectHandle CreateInstance(

string assemblyName,

string typeName,

bool ignoreCase,

BindingFlags bindingAttr,

Binder binder,

object[] args,

CultureInfo culture,

object[] activationAttributes,

Evidence securityAttributes);

Опишем параметры метода CreateInstance(). Первый параметр указывает имя сборки, содержащей тип создаваемого объекта. Сборка может быть загружена в домен, хотя это и не обязательно. Второй параметр – строка с полным именем создаваемого типа. При помощи третьего параметра можно сделать поиск типа в сборке регистронезависимым. Четвертый параметр позволяет задать область поиска конструктора типа. Если данный параметр равен нулю, выполняется регистрозависимый поиск public-конструктора типа2. Параметр binder позволяет указать особый способ связывания и подстановки вызовов методов и полей. Обычно используется способ по умолчанию (значение параметра – null). Следующим указывается массив, содержащий параметры конструктора. Параметр culture указывает культуру создаваемого объекта (актуально для приложений, настраиваемый на локализацию). Предпоследний параметр позволяет задать массив объектов-атрибутов, участвующих в активации создаваемого объекта. Последний параметр управляет атрибутами безопасности создаваемого объекта.

Метод CreateInstanceFrom() практически идентичен CreateInstance(), но сборка загружается из указанного файла. В следующем фрагменте кода происходит создание нового домена приложения и создание объекта типа Student в этом домене:

AppDomain ad2 = AppDomain.CreateDomain("New Domain");

ad2.CreateInstanceFrom(@"C:\Students.dll", // Имя файла сборки

"Student", // Имя класса

false, // Регистрозависимый поиск

// Указываем, что ищем конструктор

// Хотя с таким же успехом можно написать 0

BindingFlags.CreateInstance,

null, // Особой привязки не используем

// Параметры конструктора

new object[]{"Ivanov", 20, 4.5},

null, // Остальные параметры игнорируем

null, // или принимаем по умолчанию

null);

Приведенный фрагмент кода демонстрирует создание объекта в другом домене, однако дальнейшая работа с объектом невозможна – мы просто не имеем никакой ссылки на созданный объект. Технология передачи объектов через границы доменов (процессов) называется маршалинг. Пусть имеются два домена – D1 и D2. Предположим, что в домене D1 требуется производить работу с объектом Obj из домена D2. При маршалинге возможна передача объектов как по ссылке, так и по значению. В случае передачи по значению в домене D1 будет создана локальная копия объекта Obj из D2. Естественно, все изменения, внесенные в Obj из D1 не отразятся на Obj из D2.

В случае передачи объекта по ссылке среда исполнения использует специальный объект-прокси (посредник) Pr, который в домене D1 выглядит как объект Obj, но фактически перенаправляет все вызовы своих методов к объекту Obj в домене D2.

Простейший способ осуществить маршалинг объектов по значению – пометить тип как сериализуемый (атрибутом [Serializable]). Пусть таким образом помечен тип Student. В предыдущем примере значение, возвращаемое CreateInstance(), игнорировалось. В следующем фрагменте это значение приводится к типу Student, и демонстрируется работа с элементами созданного объекта:

using System;

using System.Reflection;

using System.Runtime.Remoting;

class MainClass {

public static void Main() {

AppDomain ad2 = AppDomain.CreateDomain("New Domain");

ObjectHandle oh = ad2.CreateInstanceFrom(

@"C:\Temp\Students.dll", "Student",

false, BindingFlags.CreateInstance,

null,

new object[]{"Ivanov", 20, 4.5},

null, null, null);

Student s = (Student)oh.Unwrap();

s.sayName();

s.Name = "Petrov";

s.sayName();

Student s_new = (Student)oh.Unwrap();

s_new.sayName();

}

}

Вот что выводит данная программа на консоль:

[New Domain]: New Student object created

[AppDom.exe]: sayName() is called

My name is Ivanov

[AppDom.exe]: sayName() is called

My name is Petrov

[AppDom.exe]: sayName() is called

My name is Ivanov

Как видим, объект был создан в одном домене, но затем копия объекта была перемещена в другой домен, где с ней и происходила работа.

Обратите внимание: метод CreateInstanceFrom() возвращает объект специального класса ObjectHandle. Данный класс реализует «обертки» над реальными объектами, что позволяет не передавать через границы доменов метаданные. Для получения реального объекта используется экземплярный метод ObjectHandle.Unwrap(), который возвращает либо объект, либо прокси. Метод CreateInstanceFromAndUnwrap() выполняет «разворачивание» созданного объекта автоматически.

Чтобы передача объектов некоторого типа между границами доменов происходила по ссылке, требуется, чтобы тип был унаследован от типа MarshalByRefObject. Если допустить, что тип Student унаследован от этого типа, то предыдущий пример выведет на консоль следующее:

[New Domain]: New Student object created

[New Domain]: sayName() is called

My name is Ivanov

[New Domain]: sayName() is called

My name is Petrov

[New Domain]: sayName() is called

My name is Petrov

В завершение приведем таблицу, содержащую некоторые методы и свойства класса System.AppDomain.

Таблица 17

Некоторые элементы класса System.AppDomain

Имя элемента

Описание

CurrentDomain

Статическое свойство, возвращающее текущий домен текущего потока

CreateDomain()

Статический метод для создания домена

GetCurrentThreadID()

Статическое свойство, возвращает идентификатор текущего потока

Unload()

Статический метод который удаляет указанный домен

FriendlyName

Строка с именем домена

DefineDynamicAssembly()

Метод позволяет динамически создавать сборки

ExecuteAssembly()

Метод запускает на выполнение указанную сборку

GetData()

Метод позволяет получить данные, сохраненные в домене под неким именем-ключом

Load()

Метод загружает сборки в домен

SetAppDomainPolicy()

Метод для установки политики безопасности домена

SetData()

Метод позволяет сохранить данные в домене под неким именем-ключом