misc.log

日常茶飯事とお仕事と

.NET、DLLの修正で参照元も全部ビルドし直すのか?

職場にて。質問を受けたのですが、即答できなかったので調べました。以前も同じような調べ物をした覚えがあるのですが、記録していなかったので自分用メモもかねて。

ちなみに開発環境はVisual Studio 2005、.NET Framework 2.0です。

厳密名を持たないDLLを修正した場合

参照元アセンブリが「何でもいいからエラー無く動く」というだけであれば、厳密名を持たないDLLを修正しても、参照元は再ビルドせずDLLだけ上書き配布できます。ただし、以下のことに注意してください。

  • Public Constなどで宣言した定数の値は参照元を再ビルドしないと反映されません。
  • Public Enumなどで宣言した列挙型の実値は参照元を再ビルドしないと反映されません。

これらについては、ビルドが通ったからといって、また、参照元が動いたからといって油断しているとハマります。注意してください。

実験してみた(Public Shared ~ As Stringとメソッドの追加)

厳密名を持たないソリューションを作成、EXE形式のプロジェクトに、クラスライブラリタイプのプロジェクトを追加して、EXEプロジェクトからプロジェクト参照でDLLを参照。DLLにはPublic Sharedな文字列変数だけが用意されていて、参照もとはそれを呼んでフォーム上のラベルに設定するだけのプログラムを作りました。この程度なら10分もかからずに作れますよね。

DLL「Message.dll」の中身はこんなです。

Public Class Message

    Public Shared MessageString1 As String = "メッセージ1"

End Class

EXEとDLLを適当なフォルダに置いて、プログラムが動くことを確認した後、DLLのソースを以下のように書き換えます。文字列提供用メンバーは増えているわ、Functionは増えているわで大改造です(笑)。ついでに、AssemblyInfo.vbも書き換えてバージョン番号もメジャーバージョンを上げましょう。

Public Class Message

    Public Shared MessageString1 As String = "メッセージ1"
    Public Shared MessageString2 As String = "メッセージ2"

    Public Function GetMessage As String
    
        Return "メッセージ A"

    End Function

End Class

で、DLLだけをビルドして、先ほどのEXEがあるフォルダに上書きコピー。動かすと……動きます。要するに、Public Sharedで宣言した変数などの変更であれば、DLLを書き換えてもDLLのビルドだけで動作上は問題ないということです。

実験してみた(定数と列挙型を持つDLLの場合)

次は、Public Constでの文字列宣言と、列挙型の宣言をDLL側で行い、それらの内容を変えた場合に何が起きるかを見てみます。先ほどはPublic Shared Stringで文字列を参照側に提供していましたが、今度は定数も増えています。列挙型も追加してみましょう。サンプルソースはこんな感じ。

f:id:frontline:20130625114948g:plain

動かすとこんな感じ。
f:id:frontline:20130625110214g:plain

これの各メンバーおよび列挙体のメンバー値をDLL側だけ変えて、ビルドし直さないEXEファイルのフォルダに配置して実行してみましょう。文字列系メンバーには「改」を付け、列挙型の値はすべて10を足しました。結果……

f:id:frontline:20130625111715g:plain

ほぅ……。定数(Const)は参照時に実際の値が埋め込まれ、動的にはDLLを見に行かないのですね。だから値が変わっていない。一方で、Public Shared宣言で公開した文字列変数は、動作時(実際はJITが動くとき?)に値が読み直されるので、DLL側の変更が反映されています。

そして、列挙型は何これ??コロンの左側はToStringをかけたもの、右側はメンバーをそのまま(あえて暗黙変換で文字列として)表示したものだったのですが、DLL差し替え後はどちらも一緒になってしまっています。値が変わらないだけならまだわかるのですが、なんで左側もこんなになったのかは別途調べますね。

結論:厳密名無しDLLで参照元全ビルドが必要なのは?

というわけで、共用するライブラリでPublicな定数等を宣言している場合は、全ビルドが必要です。逆に言うと、毎回の全ビルドを避けたいのであれば、以下の点に気をつける必要がありそうです。

  • 固定値を宣言、共用したいのであれば、Public Shared … As String、といったShared宣言をうまく使う。
  • 列挙型もDLL変更で全ビルドしないと反映されないので、列挙型のメンバー選定には十分気を遣う。なるべく変更せずにすむように設計する。

そのほかに参照元全ビルドが必要なのはどういうときか?

そのほかに全ビルドが必要なのは、ソースが以下の条件を両方満たしている場合です。

  • 参照先のDLLに「厳密名」と呼ばれる、キッチリしたバージョン/参照管理を行うための情報がくっついている。
  • DLLがビルドのたびにバージョン番号を変える運用になっていて、バージョン番号が変更された。

この場合、DLLのソース内容が変わっただけであれば呼び元(EXE側)の再ビルドは不要ですが、バージョン番号が変わった時点で参照できなくなります。これは、参照の際にDLLのバージョン番号までしっかり確認して「まさか不正なDLLと差し替えられたりしていないよな?」というチェックを行っているためです。

厳密名があってもバージョンが固定ならOKか?

結論から言うと、バージョン番号が変わらなければ、参照している側の再ビルドは不要なようです。ですが、実際の業務向けソース管理体制でバージョン管理を行わないという大きな問題が生じることから、あまり現実的な選択肢として検討しない方が良いと思います。


というわけで、たまにわからなくなって調べ直す情報のメモでした。


プログラミング .NET Framework  第3版 (マイクロソフト公式解説書)

プログラミング .NET Framework 第3版 (マイクロソフト公式解説書)

.NET Frameworkエッセンシャルズ 第2版

.NET Frameworkエッセンシャルズ 第2版