これは最近の話では無いのですが、以前トラブったことがあったので記事にしておこうと思います。
.NETでは、オプション引数とオーバーロードに違いがあるということを皆さんご存知でしょうか?
「ん?」という方もいれば、「そんなの常識だろう」と思う方もいるのではないかなと思います。
結論から言うと、「オプション引数が記述されたメソッドを含むアセンブリ」と「呼び出す側のアセンブリ」が別の場合は要注意です。
大先生がオプション引数・名前付き引数という記事で2009年に(名前付き引数といっしょに)まとめられていますので、deep diveしたい方はそちらを見ていただくとして、本記事では簡単にまとめます。
前準備
仮にこんな感じのプロジェクト・ソリューションが有ったとします。
Hamarukamo.sln +- LibProj.csproj | Class1.cs +- App.csproj | Program.cs
LibProjプロジェクトはライブラリ、Appプロジェクトはexeファイルのプロジェクトです。
AppプロジェクトがLibProjを参照している格好になります。
Class1.csは以下のように定義されています。
public class Class1
{
public decimal ZeikomiKingaku(decimal kingaku, decimal zeiritsu = 0.08m)
{
return kingaku + (kingaku * zeiritsu);
}
}
呼び出し側のProgram.csはこんな感じ。
using LibProj;
class Program
{
static void Main(string[] args)
{
Console.WriteLine(new Class1().ZeikomiKingaku(1000m));
}
}
この状態で実行すると、コンソールには1080.00と出力されます。
引数の既定値はどこにある?
さて、消費税率が10%になりました。 とうとつ
ZeikomiKingaku
メソッドの既定値を0.08から0.1に変更しましょう。
public decimal ZeikomiKingaku(decimal kingaku, decimal zeiritsu = 0.10m)
{
return kingaku + (kingaku * zeiritsu);
}
テストで税込み金額が変わっているのも確認しました。 1100.00 ok!
変更したのはLibProjだけですし、LibProj.dllだけを配布して完了!
なんてやると、ユーザーから「税込み金額が変わってないよ!」とお叱りを受けるわけですね...。
オプション引数の既定値を変えた場合、呼び出し側のApp.exeも配布しないといけません。
既定値の0.08という値は、コンパイル時に 呼び出し側に 埋め込まれます。
このため、「LibProj.dllだけ配布する」だと、App.exeの方は変わらず0.08で呼び出すことになります。
テストでうまく行ったように見えたのは、App.exeもコンパイルされていたから。
いやいや、軽減税率を考慮して呼び出し側も変更しているから、App.exeも配布するだろう!というツッコミは無しで...
まとめ
オプション引数はオーバーロードに比べて記述が簡単です。
でも、呼び出し側とアセンブリが異なる場合は要注意!です。
こういうケース「ライブラリプロジェクトではオプション引数禁止!オーバーロードで!」としちゃってもいいかもしれません。
もちろん、「ライブラリプロジェクトでも、他に公開しないメソッドの場合はお好みで」で構わないと思いますけど。
「オプション引数には罠がある(かもしれない)」くらいの意識づけは必要かなと思います。
Webプロジェクトの場合は単一のプロジェクトになることがほとんどなので、ひょっとすれば知らない方もいるかなぁと思い、記事にしてみました。
VB.NETのタグが付いているのに、ここまでVBのソース無し!と、お怒りの方、ごめんなさい。
VB.NETも同様です。
私が過去にハマったことがあったのはVB.NETを使用していた時でした。適宜、読み替えていただけると幸いです。
ちなみに、.NET Coreはどうかなぁと思い、今回試してみました(C#)が、同じでした~。
以上。
(2021/1/9追記)
最近知りましたが、こういう問題のことをバージョニング問題というらしく、public const
も同様のようです。
詳しくは大先生の定数の記事をご覧ください。
というか、オプション引数の既定値がpublic const
扱いなのでしょうね。