C#のプログラムでDI (Dependency Injection) で注入するオブジェクトのインスタンス生成処理を実装するコードを紹介します。
こちらの記事ではDI(Dependency Injection)を利用したコードを紹介しました。
クラスに注入されるオブジェクトのインスタンスはライブラリ(フレームワーク)側で生成されるため、使う側はインスタンスの生成をする必要はないですが、
注入されるオブジェクトのメンバ変数やメソッドをインスタンス生成時に設定したり呼び出したい場合があります。
この記事では、DI (Dependency Injection) で注入するオブジェクトのインスタンス生成をユーザー側で実装するコードを紹介します。
コードで注入されるオブジェクトのインスタンス処理を記述する場合は、ServiceCollection
オブジェクトにサービスを追加する際にインスタンス生成をするためのデリゲート(コールバック関数)を与えます。
下図のUIを作成します。ボタンとテキストボックスを配置します。
下記のコードを記述します。
はじめにインスタンス生成をライブラリ側で処理するコードを用意します。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DIDemoCustomInstance
{
public interface IMyService
{
void SetOption(string opt);
string GetValue();
}
public class MyService : IMyService
{
private string option;
public void SetOption(string opt)
{
option = opt;
}
public string GetValue()
{
return "ぺんぎんクッキー" + option;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Extensions.DependencyInjection;
namespace DIDemoCustomInstance
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ServiceCollection services = new ServiceCollection();
ConfigureServices(services);
ServiceProvider serviceProvider = services.BuildServiceProvider();
FormMain form = (FormMain)serviceProvider.GetRequiredService<FormMain>();
Application.Run(form);
}
private static void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<FormMain>();
services.AddSingleton<IMyService, MyService>();
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DIDemoCustomInstance
{
public partial class FormMain : Form
{
private IMyService _service;
public FormMain(IMyService service)
{
_service = service;
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text += _service.GetValue();
}
}
}
サービスクラスのインターフェイスを実装します。今回のデモでは、SetOption()
メソッドと、GetValue()
メソッドの2つを宣言します。
public interface IMyService
{
void SetOption(string opt);
string GetValue();
}
サービスクラスの実装をします。SetOption()メソッドで設定した文字列はメンバ変数に格納され、GetValue()メソッドが呼び出された際に、"ぺんぎんクッキー"の文字列の後ろに連結されて、
戻り値として返す処理になっています。
public class MyService : IMyService
{
private string option;
public void SetOption(string opt)
{
option = opt;
}
public string GetValue()
{
return "ぺんぎんクッキー" + option;
}
}
Program.csのDIに関するコードは下記です。動作の詳細はこちらのページで紹介しています。
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ServiceCollection services = new ServiceCollection();
ConfigureServices(services);
ServiceProvider serviceProvider = services.BuildServiceProvider();
FormMain form = (FormMain)serviceProvider.GetRequiredService<FormMain>();
Application.Run(form);
}
private static void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<FormMain>();
services.AddScoped<IMyService, MyService>();
}
フォームのコンストラクタに、MyService オブジェクトが注入されますので、メンバ変数に保持します。
private IMyService _service;
public FormMain(IMyService service)
{
_service = service;
InitializeComponent();
}
MyService オブジェクトの GetValue() メソッドを呼び出し、戻り値をテキストボックスに表示します。
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text += _service.GetValue();
}
プロジェクトを実行します。下図のウィンドウが表示されます。
[button1]をクリックします。テキストボックスに下図のメッセージが表示されます。
「ぺんぎんクッキー」の文字列のみ表示されます。SetOption() メソッドは呼び出されないため、option の文字列も設定されないため、動作は正しいです。
注入されるインスタンスオブジェクトのSetOption() メソッドを呼び出してOptionの文字列を設定しておくようにしたいです。
ConfigureServicesメソッドのコードを以下のコードに変更します。また、cfuncメソッドを追加します。
private static void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<FormMain>();
services.AddScoped<IMyService, MyService>(cfunc);
}
private static MyService cfunc(IServiceProvider provider)
{
MyService ms = new MyService();
ms.SetOption(":在庫あり");
return ms;
}
サービスクラスをServiceCollectionに追加する際に呼び出す、AddScoped() メソッドの引数に、サービスクラスをインスタンス化する処理のデリゲートを与えます。
services.AddScoped<IMyService, MyService>(cfunc);
デリゲートのコードは以下のコードです。
MyService クラスのインスタンスを作成し、SetOption()メソッドを呼び出し、「在庫あり」の文字列をOption文字列に設定します。
private static MyService cfunc(IServiceProvider provider)
{
MyService ms = new MyService();
ms.SetOption(":在庫あり");
return ms;
}
上記のコードに変更後、プロジェクトを実行し、[button1]をクリックします。
テキストボックスに下図のメッセージが表示されます。
「ぺんぎんクッキー:在庫あり」の文字列が表示されます。cfuncのインスタンス生成のデリゲートが実行され、
注入されるオブジェクトのOption 文字列に ":在庫あり" が設定されていることが確認できます。
なお、デリゲートは再利用されないメソッドとなるため、通常はラムダ式でコードを記述します。
次のコードになります。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Extensions.DependencyInjection;
namespace DIDemoCustomInstance
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ServiceCollection services = new ServiceCollection();
ConfigureServices(services);
ServiceProvider serviceProvider = services.BuildServiceProvider();
//FormMain form = (FormMain)serviceProvider.GetRequiredService<FormMain>();
//FormMain form = (FormMain)serviceProvider.GetService<FormMain>();
//FormMain form = (FormMain)serviceProvider.GetService(typeof(FormMain));
FormMain form = (FormMain)serviceProvider.GetRequiredService(typeof(FormMain));
Application.Run(form);
}
private static void ConfigureServices(ServiceCollection services)
{
services.AddSingleton<FormMain>();
services.AddScoped<IMyService, MyService>(pvd => {
MyService ms = new MyService();
ms.SetOption(":在庫なし");
return ms;
});
}
}
}
上記のコードを実行し、[button1]ボタンをクリックするとテキストボックスに「ぺんぎんクッキー:在庫なし」の結果が表示されます。