🗿

[C#] DIでServiceCollectionに同じInterfaceのクラスを複数登録する(.NET8~)

に公開2

もくじ
https://tera1707.com/entry/2022/02/06/144447

やりたいこと

以前、DIでServiceCollectionに同じInterfaceのクラスを複数登録してコンストラクタで受け取る方法を調べた。

https://tera1707.com/entry/2023/02/02/230500

.NET8以降だと、AddKeyedTransientという拡張メソッドで、仕組みとして用意されたらしい。試してみる。

実験コード

登録する部分

以前作った、C#,C++などののバージョンを表示するアプリのコードの一部。

https://github.com/tera1707/FileVerUpTool/blob/5fa6da75904c9185d7d958988c24e46108b520ba/FileVerUpTool/FileVerUpTool/App.xaml.cs#L56

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();
}

使う部分

https://github.com/tera1707/FileVerUpTool/blob/5fa6da75904c9185d7d958988c24e46108b520ba/FileVerUpTool/FileVerUpTool/Logic/VersionReadWrite.cs#L24

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

tera1707tera1707

@junar さん、コメントありがとうございます。
これを知るまでは

 services.AddSingleton<IProjMetaDataHandler[]>

こんな感じでやってたんですが、IEnumerable<interfaceの型> も行けそうですね。

IProjMetaDataHandler[] でやってた時は、なにか裏技?的なやり方をしている感覚だったのですが、公式なやり方ができたようでありがたいですw