🗿
[C#] DIでServiceCollectionに同じInterfaceのクラスを複数登録する(.NET8~)
もくじ
やりたいこと
以前、DIでServiceCollectionに同じInterfaceのクラスを複数登録してコンストラクタで受け取る方法を調べた。
.NET8以降だと、AddKeyedTransientという拡張メソッドで、仕組みとして用意されたらしい。試してみる。
実験コード
登録する部分
以前作った、C#,C++などののバージョンを表示するアプリのコードの一部。
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
// ServiceCollectionを作成
var services = new ServiceCollection();
// インスタンスを登録 ★👇ここが登録する部分
services.AddKeyedTransient<IProjMetaDataHandler, SdkTypeCsprojHandler>(typeof(SdkTypeCsprojHandler));
services.AddKeyedTransient<IProjMetaDataHandler, DotnetFrameworkProjHandler>(typeof(DotnetFrameworkProjHandler));
services.AddKeyedTransient<IProjMetaDataHandler, CppProjHandler>(typeof(CppProjHandler));
services.AddKeyedTransient<IProjMetaDataHandler, AppxManifestHandler>(typeof(AppxManifestHandler));
services.AddSingleton<IVersionReadWrite, VersionReadWrite>();
services.AddSingleton<MainWindow>();
// サービスプロバイダ、サービスを作成
var provider = services.BuildServiceProvider();
Ioc.Default.ConfigureServices(provider);
// 登録したインスタンスを使う
var mw = Ioc.Default.GetRequiredService<MainWindow>();
mw.Activate();
}
使う部分
public VersionReadWrite(
[FromKeyedServices(typeof(SdkTypeCsprojHandler))] IProjMetaDataHandler sdk,
[FromKeyedServices(typeof(DotnetFrameworkProjHandler))] IProjMetaDataHandler dfw,
[FromKeyedServices(typeof(CppProjHandler))] IProjMetaDataHandler cpp,
[FromKeyedServices(typeof(AppxManifestHandler))] IProjMetaDataHandler appxm)
{
_sdkcsproj = sdk;
_dfwcsproj = dfw;
_cppproj = cpp;
_appxmanifest = appxm;
}
メモ
services.AddKeyedTransient<IProjMetaDataHandler, SdkTypeCsprojHandler>(typeof(SdkTypeCsprojHandler));
この部分のキー「typeof(SdkTypeCsprojHandler)」は、最初「nameof(SdkTypeCsprojHandler)」と書いてたのだが、それはお勧めできないらしい。
AIによると、

で、それ以外にもいくつかやり方があるが、👇の中だと、enumでやるのが一番お勧めとのこと。

※今回の私のコードは、上でいうと「Type」にあたる。
※stringの「タイポに弱い」は、キーの文字列を直接打ち込んだときのこと。

typeofよりenumが推奨の理由
「意味」で定義したenumを使うから、変更(クラス名が変わった、とか)にも強いということか。


もしenumで行くとしたら、interfaceと同じcsファイルに、そのenumも定義して使えばよいかな、と思った。
Discussion
interface に複数登録して IEnumerable<interfaceの型> として全部取得するのも手ですね。(識別する必要が無い場合
@junar さん、コメントありがとうございます。
これを知るまでは
こんな感じでやってたんですが、
IEnumerable<interfaceの型>も行けそうですね。IProjMetaDataHandler[]でやってた時は、なにか裏技?的なやり方をしている感覚だったのですが、公式なやり方ができたようでありがたいですw