拡張可能オブジェクト

拡張可能オブジェクト パターンは、既存のランタイム クラスに新しい機能を付加して拡張したり、オブジェクトに新しい状態を追加するために使用します。 このようなオブジェクトを実際に拡張することにより、処理の段階に応じて、共通の拡張可能オブジェクトに定義された共有の状態や機能にアクセスすることができます。

IExtensibleObject<T> パターン

拡張可能オブジェクト パターンには、IExtensibleObject<T>IExtension<T>、および IExtensionCollection<T> の 3 つのインターフェイスがあります。

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> であるクラスに対してしか拡張を定義できないことを保証します。 AttachDetach は、集約や集約解除に関する通知を提供します。

所有者が追加や削除を行う際の制限を組み込むことも可能です。 たとえば、削除は全面的に禁止する、所有者や拡張がある状態であれば拡張の追加や削除を禁止する、同時に複数の所有者に追加することを禁止する、1 つ削除したら 1 つだけ追加を許可する、などの制約を指定することができます。

IExtension<T> を実装しても、他の標準のマネージド インターフェイスとやりとりできるとは限りません。 特に、所有者オブジェクトの IDisposable.Dispose メソッドは、自分自身の拡張を解除しないのが普通です。

拡張機能がコレクションに追加されると、コレクションに入る前に Attach が呼び出されます。 コレクションから拡張機能を削除すると、削除された後に Detach が呼び出されます。 つまり、(同期が適切であると想定すると、) 拡張機能がコレクション内で見つかるのは、AttachDetach の間のみと考えることができます。

FindAllFind に渡すオブジェクトは、IExtension<T> である必要はなく、任意のオブジェクトを渡すことができますが、返される拡張は IExtension<T> です。

コレクション内の拡張機能がいずれも IExtension<T> ではない場合は、Find が null を返し、FindAll は空のコレクションを返します。 複数の拡張機能が IExtension<T> を実装している場合、Find は、そのうちの 1 つを返します。 FindAll から返される値はスナップショットです。

主な使い方として、次の 2 つのシナリオが考えられます。 1 つ目のシナリオでは、型ベースのディクショナリとして Extensions プロパティを使用し、オブジェクトに状態を追加します。これは、別のコンポーネントが型に基づいて検索できます。

2 つ目のシナリオでは、Attach プロパティおよび Detach プロパティを使用し、イベントへの登録や、状態の遷移の監視など、カスタムの動作にオブジェクトを参加させます。

IExtensionCollection<T> インターフェイスは、型を基準にした IExtension<T> の取得を可能にする IExtension<T> オブジェクトのコレクションです。 IExtensionCollection<T>.Find は、指定された型の IExtension<T> オブジェクトのうち、最も新しく追加されたものを返します。

Windows Communication Foundation の拡張可能オブジェクト

Windows Communication Foundation (WCF) には、次の 4 つの拡張可能オブジェクトがあります。

  • ServiceHostBase : サービス ホストの基本クラスです。 ServiceHostBase 自身の動作を拡張するほか、各サービスの状態を保存しておくための拡張が可能です。

  • InstanceContext : サービスのランタイムを使用して、サービスの型のインスタンスと接続するためのクラスです。 このクラスは、インスタンスに関する情報のほか、InstanceContext に含まれる ServiceHostBase への参照を保持します。 InstanceContext 自身の動作を拡張するほか、各サービスの状態を保存しておくための拡張が可能です。

  • OperationContext : 操作ごとにランタイムが収集した操作情報を表すクラスです。 具体的には、受け取ったメッセージのヘッダーやプロパティ、受け取ったセキュリティ ID などの情報があります。 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();
    }
  }
}

関連項目