Расширяемые объекты

Шаблон расширяемого объекта используется для расширения существующих классов среды выполнения при помощи новых функций или добавления нового состояния к объекту. Расширения, привязанные к одному из расширяемых объектов, позволяют использовать поведения на различных этапах обработки для получения доступа к общему состоянию и функциональности, привязанным к общему расширяемому объекту, к которому они могут получить доступ.

Шаблон T> IExtensibleObject<

Предусмотрено три интерфейса в шаблоне расширяемого объекта: IExtensibleObject<T>, IExtension<T> и IExtensionCollection<T>.

Интерфейс IExtensibleObject<T> реализуется типами, которые позволяют объектам IExtension<T> настроить свою функциональность.

Расширяемые объекты обеспечивают динамическое агрегирование объектов IExtension<T>. Объекты IExtension<T> характеризуются следующим интерфейсом:

public interface IExtension<T>
where T : IExtensibleObject<T>
{
    void Attach(T owner);
    void Detach(T owner);
}

Ограничение типов гарантирует, что расширения можно определить только для классов IExtensibleObject<T>. МетодыAttach и Detach предоставляют уведомление об агрегировании или деагрегировании.

Реализации могут ограничивать, если их можно добавить и удалить из владельца. Например, можно полностью запретить удаление, запретить добавление или удаление расширений, когда владелец или расширение находится в определенном состоянии, запретить одновременное добавление в несколько владельцев или разрешить только одно добавление с последующим одним удалением.

В шаблоне IExtension<T> не подразумевается никаких взаимодействий с другими стандартными управляемыми интерфейсами. В частности, метод IDisposable.Dispose в объекте владельца, как правило, не отсоединяет его расширения.

При добавлении расширения в коллекцию Attach вызывается перед переходом в коллекцию. Когда расширение удаляется из коллекции, Detach вызывается после его удаления. Это означает , что (при условии соответствующей синхронизации) расширение может рассчитывать только на то, что оно найдено в коллекции, пока оно находится между Attach и Detach.

Нет необходимости, чтобы объект, переданный методу FindAll или методу Find, был объектом IExtension<T> (например, можно передать любой объект), но возвращенное расширение должно быть расширением IExtension<T>.

Если расширение в коллекции не является IExtension<T>, Find возвращает значение NULL и FindAll возвращает пустую коллекцию. Если реализуется IExtension<T>несколько расширений, Find возвращает одно из них. Значение, возвращаемое методом FindAll, является моментальным снимком.

Имеется два основных сценария. В первом сценарии свойство Extensions используется как основанный на типах словарь для вставки состояния в объект с целью предоставления другому компоненту возможности просматривать его с помощью типа.

Во втором сценарии свойства Attach и Detach используются для предоставления объекту возможности участвовать в пользовательском поведении, таком как регистрация событий, наблюдение за переходами между состояниями и т. д.

Интерфейс IExtensionCollection<T> - это коллекция объектов IExtension<T>, которая позволяет получить экземпляр IExtension<T> по его типу. Метод IExtensionCollection<T>.Find возвращает последний добавленный объект IExtension<T> данного типа.

Расширяемые объекты в Windows Communication Foundation

В Windows Communication Foundation (WCF) есть четыре расширяемых объекта:

  • ServiceHostBase. Это базовый класс для узла службы. Расширения этого класса можно использовать для расширения поведения самого класса ServiceHostBase или для хранения состояния для каждой службы.

  • InstanceContext. Этот класс соединяет экземпляр типа службы и среду выполнения службы. В нем содержится информация об экземпляре, а также ссылка на класс InstanceContext, содержащий класс ServiceHostBase. Расширения этого класса можно использовать для расширения поведения самого класса InstanceContext или для хранения состояния для каждой службы.

  • OperationContext. Этот класс представляет данные об операциях, собранные средой выполнения для каждой операции. Сюда входят такие данные как заголовки входящих сообщений, свойства входящих сообщений, идентификация входящих сообщений и др. Расширения этого класса можно использовать как для расширения поведения класса OperationContext, так и для хранения состояния для каждой операции.

  • IContextChannel — этот интерфейс позволяет проверять каждое состояние для каналов и прокси-серверов, созданных средой выполнения WCF. Расширения этого класса можно использовать как для расширения поведения класса IClientChannel, так и для хранения состояния для каждого канала.

В следующем примере кода показано использование простого расширения для отслеживания объектов InstanceContext.

using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.Text;

namespace Microsoft.WCF.Documentation
{
  public class MyInstanceContextInitializer : IInstanceContextInitializer
  {
    public void Initialize(InstanceContext instanceContext, Message message)
    {
      MyInstanceContextExtension extension = new MyInstanceContextExtension();

      //Add your custom InstanceContext extension that will let you associate state with this instancecontext
      instanceContext.Extensions.Add(extension);
    }
  }

  //Create an Extension that will attach to each InstanceContext and let it retrieve the Id or whatever state you want to associate
  public class MyInstanceContextExtension : IExtension<InstanceContext>
  {

    //Associate an Id with each Instance Created.
    String instanceId;

    public MyInstanceContextExtension()
    { this.instanceId = Guid.NewGuid().ToString(); }

    public String InstanceId
    {
      get
      { return this.instanceId; }
    }

    public void Attach(InstanceContext owner)
    {
      Console.WriteLine("Attached to new InstanceContext.");
    }

    public void Detach(InstanceContext owner)
    {
      Console.WriteLine("Detached from InstanceContext.");
    }
  }

  public class InstanceInitializerBehavior : IEndpointBehavior
  {

    public void AddBindingParameters(ServiceEndpoint serviceEndpoint, BindingParameterCollection bindingParameters)
    {    }

    //Apply the custom IInstanceContextProvider to the EndpointDispatcher.DispatchRuntime
    public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher)
    {
      MyInstanceContextInitializer extension = new MyInstanceContextInitializer();
      endpointDispatcher.DispatchRuntime.InstanceContextInitializers.Add(extension);
    }

    public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior)
    {    }

    public void Validate(ServiceEndpoint endpoint)
    {    }
  }

  public class InstanceInitializerBehaviorExtensionElement : BehaviorExtensionElement
  {

    public override Type BehaviorType
    {
      get { return typeof(InstanceInitializerBehavior); }
    }

    protected override object CreateBehavior()
    {
      return new InstanceInitializerBehavior();
    }
  }
}

См. также