Выбрать главу

Можно выделить две компонентные модели, поддерживаемые технологией .NET. Во-первых, это уровень CLR (Common Language Runtime) — основа всей технологии .NET. Данная модель наиболее близка к СОМ. Во-вторых, это очень популярная сегодня модель XML Web-сервисов, в рамках которой возможно взаимодействие программ, исполняющихся на разных платформах при условии, что взаимодействующие программы понимают протокол SOAP.

Здесь мы будем говорить только о первой модели. Это представляется вполне естественным при переходе от таких тем как СОМ и СОМ+. И изложение будет основано на сопоставлении компонентной модели СОМ и модели, поддерживаемой в CLR.

Интерфейсы

Интерфейсы являются основой СОМ. Там их роль состоит в изоляции клиента от компонента, благодаря чему клиент без перекомпиляции может работать с разными версиями компонента. Кроме того, интерфейсы обеспечивают в СОМ независимость от языка программирования, на котором написаны клиент и компонент.

В СОМ и в СОМ+ мы видим огромное множество интерфейсов, совокупность которых можно рассматривать как некоторое компонентно — ориентированное расширение базового языка программирования (например, C++). Стоит отметить, что изучать эти интерфейсы весьма не просто в силу их многочисленности и отсутствия единой систематизирующей структуры.

Роль интерфейсов в .NET резко сокращена. На взгляд автора, теперь интерфейсы прежде всего служат для расширения множества типов, приписанных данному классу. В отличие от СОМ, где не было наследования реализации, в .NET класс может наследовать одному классу (по умолчанию классу System.Object) и любому числу интерфейсов, которые не имеют реализации и не наследуют корневому классу System.Object.

Относительное снижение значимости интерфейсов в .NET по сравнению с СОМ в .NET компенсируется следующим:

• Клиент больше не изолируется от компонента

Если в СОМ клиент был максимально изолирован от компонента, то в .NET ему доступна разнообразная информация о компоненте. Это обеспечивается тем, что в коде компонента хранятся описывающие его метаданные, доступ к которым клиент может получить используя механизм рефлексии.

• Многие интерфейсы более не нужны

Многие интерфейсы, играющие важную роль в СОМ, более не нужны в .NET. С помощью представленного ниже примера будет объяснено, почему, например, стал не нужен базовый интерфейс для СОМ — IUnknown. Те возможности, которые ранее программисты получали за счет реализации и использования большого числа интерфейсов, теперь можно получить за счет использования среды разработки и исполнения, предоставляемой .NET. Это и CLR, и общая система типов CTS, и промежуточный язык MSIL, на который транслируются программы со всех других языков, поддержанных .NET.

Сервер в процессе клиента

Рассмотрим в качестве примера процесс построения и использования сервера, который в рамках COM-терминологии получил бы классификацию "сервер в процессе клиента", т. к. он будет исполняться в адресном процессе клиента. Код представлен на С#.

Сервер

using System;

namespace MyServer {

public interface IAccumulator {

void Add(int sum);

}

public interface IAudit {

        int Total();

}

public class Account: IAccumulator, IAudit {

        protected int _sum = 0;

        public void Add(int sum) {

             _sum += sum;

        }

        public int Total() {

             Console.WriteLine("Server AppDomain = {0}",

                 AppDomain.CurrentDomain.FriendlyName);

             return _sum;

        }

   }

}

Клиент

using System;

using System.Reflection;

using MyServer;

public class MyApp {

public static void Main() {

       Account a = new Account();

       IAccumulator iAcc = a as IAccumulator;

       if (iAcc!= null) {

           iAcc.Add(3);

           iAcc.Add(5);

       }

       IAudit iAud = iAcc as IAudit;

       if (iAud!= null)

           Console.WriteLine("Total = {0}", iAud.Total());

       Console.WriteLine("Client AppDomain = {0}\n",

              AppDomain.CurrentDomain.FriendlyName);

       Type iAuditType = iAud.GetType();

       Methodlnfо[] Methods = iAuditType.GetMethods();

       foreach (MethodInfо Method in Methods)!

       Console.WriteLine(Method.ToString());

       }

    }

}

В данном примере сервер реализован классом Account. Этот класс реализует два интерфейса:

 IAccumulator

Этот интерфейс позволяет клиенту отправлять на счет, поддерживаемый сервером, некоторую сумму (метод Add)

• IAudit

Данный интерфейс позволяет клиенту узнать текущее состояние счета (метод Total)

И интерфейсы, и реализующий их класс Account определяются в рамках одного пространства имен MyServer.

Стоит еще обратить внимание на то, что метод Total не только возвращает текущую сумму на счете, но и выводит на консоль имя домена приложения, в котором выполняется сервер. Тут необходимы дополнительные пояснения. Управляемый код, порождаемый компилятором с C# и использующий все сервисы CLR, является типо-безопасным кодом, который в процессе своего выполнения не может повредить данные, к которым он не должен иметь доступа. Это позволяет в рамках одного процесса организовать несколько логических процессов — доменов приложений (Application Domain), которые делят одно адресное пространство и границу между которыми может пересекать поток. Каждое приложение работает в рамках одного домена приложения. За счет "легковестности" доменов приложений, их использование более эффективно, чем использование для каждого приложения отдельного процесса. Выводимая на консоль информация об имени домена приложения позволит нам проверить, что и клиент и сервер действительно выполняются в рамках одного домена приложения.

Теперь обратимся к клиенту. Используется пространство имен MyServer, в котором определен класс Account. Кроме того, используется пространство имен System. Reflection. В этом пространстве определены классы, обеспечивающие механизм рефлексии, т. е. доступ к метаданным.

Клиентское приложение является консольным приложением. Функция Main обеспечивает точку входа. Оператор new обеспечивает построение экземпляра класса Account. При этом нет необходимости предварительно регистрировать сервер в реестре и при его активации задавать соответствующий CLSID. Нет и никаких фабрик классов.

Во-первых, теперь нет нужды в методе QueryInterface. Оператор as позволяет безопасно привести ссылку на объект к ссылке на любой реализуемый им интерфейс. Если полученная ссылка на интерфейс IAccumulator не равна null, то это означает, что класс Account действительно реализует интерфейс IAccumulator и клиент может отправить на счет две суммы (3 и 5), используя метод Add.

Теперь, имея ссылку на интерфейс IAccumulator, можно перейти к ссылке на интерфейс IAudit. В случае реализации этого интерфейса вызывается его метод Total и на консоль выводится сумма счета и имя домена приложения, в котором выполняется клиент.