もくじ
https://tera1707.com/entry/2022/02/06/144447
やりたいこと
nugetで参照するあるパッケージ(ライブラリ)の異なるバージョンを、一つのexeから切り替えてつかいたい。
基本、一つのexeでnugetで参照できる1つのパッケージの複数のバージョンを同時に使うようなことはできないと思っているのだが、それでも何とかうまいことできないか。試してみる。
前提
- VisualStudio2022 v17.3.0
- .NET6
実験リポジトリ
プロジェクト名が長すぎて、普通にデスクトップとかにクローンすると、なんかうまくビルドできなくなるかも。
→topフォルダ名を短くするとかして試してください。
やったこと
概要
exeでnugetパッケージを直接参照しないようにする。
代わりに、そのパッケージを参照する2つの窓口ライブラリプロジェクトを作成し、そこで2つのバージョンのnugetパッケージを参照させる。
で、exeからはその2つの窓口ライブラリを切り替えて使うことで、何とかうまいことできないかやってみた。
図で書くと下記のようなイメージ。
詳細
- 窓口となるクラスが実装することになるインターフェースを作成する
- ILogWriter1:Ver1用のIF。Write()メソッドをもつ。
- ILogWriter2:Ver2用のIF。Write2()メソッドをもつ。
- 窓口となるクラスライブラリのプロジェクトをつくり、上記IFを実装するクラスを作成し、IFのPJをPJ参照に入れ、IFのメソッドを実装する。
- LogWriter1:ILogWriter1を実装するクラス。
- LogWriter2:ILogWriter2を実装するクラス。
- その中に、
- LogWriter1をもつプロジェクトで、NLogのv4.7.15をnugetで参照に入れる。
- LogWriter2をもつプロジェクトで、NLogのv5.0.4をnugetで参照に入れる。
※ビルド構成マネージャーで、すべてのPJについて、プラットフォームをx64のみにして、ほかのプラットフォームを削除しておく。
下記3つの設定を、ClassLibrary1,2のcsprojの
<PropertyGroup>
に入れる。<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
※これだけ、exeとIFのPJにはいれない
LogWriter1,2のプロジェクトの、出力先フォルダを、exeの出力先フォルダにフォルダを掘ってそこに入れるようにする。 今回の構成だとそれぞれ下記にする。
..\SameLibraryDifferentVersionJikken\bin\$(Platform)\$(configuration)\lib1
..\SameLibraryDifferentVersionJikken\bin\$(Platform)\$(configuration)\lib2
exeのコードに、呼び分けるコードを書く。
- 読み込みたいほうのdllを、
Assembly.LoadFrom(読みたい方のdllのフルパス)
でロードする - その中のクラスを
asm?.CreateInstance(名前空間名.クラス名)
で読み込む - 読み込んだクラスのオブジェクトを、Interfaceでキャストする
- そのオブジェクトで、呼びたいメソッドを呼ぶ
- 読み込みたいほうのdllを、
これで、やりたいことができた。
exeと同じフォルダにexeが参照しているdllがさらに参照しているライブラリ(今回の場合はNlog.dll)が存在しないが、 うまくそれぞれの(lib1/lib2フォルダの下にある)NLog.dllを読んでくれてるっぽい。
ClassLibrary1.dllを使ったときに読み込まれるNLog.dll
ClassLibrary1.dllを使ったときに読み込まれるNLog.dll
フォルダ構成
フォルダ構成は下記のような感じ。
備考
exeのPJから、LogWriter1と2のPJを参照してしまうと、exeの出力先にLogWriter1と2が参照するNLog.dllがコピーされてしまう。
そのとき、LogWriter1と2が同じ名前のNLog.dllを参照していてそれがコピーされるので、たまたま後でコピーされたほうだけが残ってしまい、片方が消えるので、直接参照できない。なのでわざわざlib1,2フォルダを掘って、動的にLoadFromで読み込むようにした。
exeからクラスを直接参照しないので、読み込んだクラスの型がわからずインテリセンスが効かないが、それだと実装時の効率がわるいので、インターフェース(ILogWriter1,2)を設けてインテリセンスが効くようにした。
※読み込んだクラスをdynamic
にして、インテリセンス聞かないまま実装もできるが、そこは避けてインターフェースを作る手間の方を取った。
またILogWriter2は、ILogWriter1の機能UP版、みたいなイメージで、ILogWriter1を継承する感じにしているが、全く別のIFにしてもいいと思う。
参考
AppendTargetFrameworkToOutputPath
、AppendRuntimeIdentifierToOutputPath
、CopyLocalLockFileAssemblies
などのcsprojに追記した項目は下記に説明がある。
アセンブリの読み込み方のベストプラクティス
とあるが、うーん、むずかしい、、、Assembly。LoadFrom()をしたときに、Loadしたdllが依存するdllが同じ階層にあったらそっちを呼んでくれる動作をすることの裏を取りたかったのだが、なかなか下記ページを読み解けない。。。
参考書
WinUI3
WinUI3でアプリを作ろうと思ったときのとっかかりによかった。
msdocsに書いてある情報を、体系的に、順番に読みたいな、というときによいかも。(ただし英語)
この本で分からなかった、かゆいところに手が届かなかった部分を私は記事にしてる感じ。
C#①
表紙に書いてある通り、教科書として最適。
これからC#を勉強したいけど、ネットだけで勉強するのは効率が悪いから体系的に学べる本が欲しいときや、
ちょっとC#を勉強してコード書けるようになったけど、もう少し広く深く知りたいなというときによいと思う。
私は仕事で触れるコードを軸に、基本ネットで断片的にC#を学んだので、その知識の隙間を埋めて枝葉を広げるためにとても分かりやすかった。
C#②
C#の文法的に色々できるのは分かったが、いざ実装するときに、わかったことを使ってどう実装すればいいのか?と悩んだときに指針になりそうな本。
「プロパティ等の名前の付け方、どうすればいい?」「情報をクラス外部に見せるときに、プロパティにすべき?メソッドにすべき?」「異常だと判定したいとき、どんなときにどんな例外をスローすべき?」などなど、勉強になる部分が山ほどあった。
私のように「コードは書くけどこれであってるのか自信がない、レビューで指摘されるのが嫌だ、実装時の(心の)よりどころが欲しい」という人に最適。