Managed Extensibility Framework(MEF)入門 -Lazy(遅延エクスポート)-

MEFではサービスのエクスポートを遅延させる事が出来ます。コンテナーに登録されたサービスを取得する場合、コントラクトと取得型を指定して希望のサービスを取得するか、コンテナーよりパーツにサービスをインポートしてもらいサービスを使用するのが一般的です。ですが、取得対象のサービスのインスタンスが大きかったりインポートされるサービス数が膨大にある場合はメモリーに圧迫をかけてしまう事になります。こういった場合に「遅延エクスポート」という機構を使用する事によって、実際にサービスを使用する場合にのみインスタンスの生成を行わせる事が出来ます。遅延エクスポートを使用する場合には『System.Lazy<T>・System.Lazy(Of T)』クラスを使用します。

 

lazy_details

 

遅延エクスポートを使用するには、図のようにエクスポートされたサービスをインポート時、又はコンテナーより取得する際にLazyクラスに内包する形で取得します。Lazyクラスの『Valueプロパティ』を通じてサービスを取得します。LazyクラスはValueプロパティにアクセスされるまでサービスのインスタンスの生成を遅延させます。Valueプロパティにサービスインスタンスが生成されているか判別するプロパティの『IsValueCreatedプロパティ』も用意されています。これをサンプルコードを元に見てみましょう。

C#
    1 using System;
    2 using System.ComponentModel.Composition;
    3 using System.ComponentModel.Composition.Hosting;
    4 
    5 namespace LazyExport.CSharp
    6 {
    7    public class Program
    8    {
    9       private CompositionContainer container;
   10 
   11       [Import]
   12       public Lazy<IMessageSender> MessageSender { get; set; }
   13 
   14       private Program()
   15       {
   16          this.container = new CompositionContainer();
   17          CompositionBatch batch = new CompositionBatch();
   18          batch.AddPart(new MessageSender());
   19          batch.AddPart(this);
   20          this.container.Compose(batch);
   21       }
   22 
   23       private void Run()
   24       {
   25          //コンテナーより直接取得したLazyクラスを使用。
   26          Lazy<IMessageSender> messageSender = this.container.GetExport<IMessageSender>();
   27          Console.WriteLine(string.Format("instance is created = {0}", messageSender.IsValueCreated.ToString()));
   28          Console.WriteLine(messageSender.Value.GetMessage());
   29          Console.WriteLine(string.Format("instance is created = {0}", messageSender.IsValueCreated.ToString()));
   30 
   31          Console.WriteLine();
   32 
   33          //インポートされたLazyクラスを使用。
   34          Console.WriteLine(string.Format("instance is created = {0}", this.MessageSender.IsValueCreated.ToString()));
   35          Console.WriteLine(this.MessageSender.Value.GetMessage());
   36          Console.WriteLine(string.Format("instance is created = {0}", this.MessageSender.IsValueCreated.ToString()));
   37       }
   38 
   39       public static void Main(string[] args)
   40       {
   41          Program program = new Program();
   42          program.Run();
   43       }
   44    }
   45 
   46    public interface IMessageSender
   47    {
   48       string GetMessage();
   49    }
   50 
   51    [Export(typeof(IMessageSender))]
   52    public class MessageSender : IMessageSender
   53    {
   54       public string GetMessage()
   55       {
   56          return "メッセージです。";
   57       }
   58    }
   59 }
VB
    1 Imports System
    2 Imports System.ComponentModel.Composition
    3 Imports System.ComponentModel.Composition.Hosting
    4 
    5 Public Class Program
    6    Private container As CompositionContainer
    7 
    8    <Import()> _
    9    Public Property MessageSender As Lazy(Of IMessageSender)
   10 
   11    Private Sub New()
   12       Me.container = New CompositionContainer()
   13       Dim batch As New CompositionBatch()
   14       batch.AddPart(New MessageSender())
   15       batch.AddPart(Me)
   16       Me.container.Compose(batch)
   17    End Sub
   18 
   19    Private Sub Run()
   20       'コンテナーより直接取得したLazyクラスを使用。
   21       Dim messageSender As Lazy(Of IMessageSender) = Me.container.GetExport(Of IMessageSender)()
   22       Console.WriteLine(String.Format("instance is created = {0}", messageSender.IsValueCreated.ToString()))
   23       Console.WriteLine(messageSender.Value.GetMessage())
   24       Console.WriteLine(String.Format("instance is created = {0}", messageSender.IsValueCreated.ToString()))
   25 
   26       Console.WriteLine()
   27 
   28       'インポートされたLazyクラスを使用。
   29       Console.WriteLine(String.Format("instance is created = {0}", Me.MessageSender.IsValueCreated.ToString()))
   30       Console.WriteLine(Me.MessageSender.Value.GetMessage())
   31       Console.WriteLine(String.Format("instance is created = {0}", Me.MessageSender.IsValueCreated.ToString()))
   32    End Sub
   33 
   34    Public Shared Sub Main()
   35       Dim program As New Program()
   36       program.Run()
   37    End Sub
   38 End Class
   39 
   40 Public Interface IMessageSender
   41    Function GetMessage() As String
   42 End Interface
   43 
   44 <Export(GetType(IMessageSender))> _
   45 Public Class MessageSender
   46    Implements IMessageSender
   47    Public Function GetMessage() As String Implements IMessageSender.GetMessage
   48       Return "メッセージです。"
   49    End Function
   50 End Class

上記サンプルコードを実行すると以下のような結果が出力されます。

 

lazy_details_output

 

出力結果から分かるとおりにLazyインスタンスValueプロパティにアクセスするまでインスタンスが存在していない事が分かります。ValueプロパティにアクセスされるとLazyクラスはサービスのインスタンスを生成します。このようにエクスポートされたサービスを遅延させる事でコンテナーでエクスポートされたサービスのインスタンスを常にメモリに持っておく必要が無くなるのです。サービスインスタンスが大きい場合は即時使用されないサービス等がある場合はLazyクラスを上手く利用すると良いでしょう。

 

Source code