19
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【C#】デフォルト引数を追加したらアプリケーションが動かなくなった

Posted at

はじめに

メソッドの呼び出し側を修正することなく、機能を追加できるデフォルト引数(オプション引数)は便利ですが、使いどきを誤るとアプリケーションに予期しない影響を及ぼすことがあります。

この記事では私が実務でデフォルト引数を追加した際にアプリケーションが動かなくなった体験を元に、デフォルト引数の追加時の注意点と解決方法をまとめます。

この記事の対象者

  • C#を使って開発をしている人

アプリケーションが動かなくなった状況

下の図のように1つのソリューションに共通メソッドを定義するプロジェクトと、それを利用する複数のプロジェクトがある状況で、共通メソッドの動作を変更する修正案件がありました。
image.png

共通メソッドは以下のような税込み価格を返すメソッドだったとしましょう。

CommonLib.cs
public static class CommonLib
{
    public static decimal CalcTaxIncludedPrice(decimal price)
    {
        return price * 1.08M;
    }
}

プロジェクトAからの呼び出しのときだけ、税率を変更したかったため、下記のようにデフォルト引数を追加して税率を変更できるようにしました。

CommonLib.cs
public static class CommonLib
{
    public static decimal CalcTaxIncludedPrice(decimal price, decimal rate = 1.08M)
    {
        return price * rate;
    }
}

動作検証も完了し、担当者に修正したコードを渡して、ホッと一息ついていると、担当者が動作検証したところ、プロジェクトBの機能でエラーが起きるとの報告が来ました。

私の環境では正しく動作していたため、何が起きたのか確認すると、担当者はプロジェクトAと共通メソッドプロジェクトのみをリコンパイルして動作検証したとのことでした。

何が起きたのか

デフォルト引数を追加した場合、呼び出し側に定数が埋め込まれる形で解釈されます。

// デフォルト引数を書いていないこのコードは
var taxIncludedPrice = CommonLib.CalcTaxIncludedPrice(100);

// コンパイル時にこのようなコードに解釈される
var taxIncludedPrice = CommonLib.CalcTaxIncludedPrice(100, 1.08M);

そのため、リコンパイルしていないプロジェクトBでは、定数が埋め込まれておらず、引数が1つのCalcTaxIncludedPriceメソッドを呼んでしまうため、実行すると以下のようなエラーが起きてしまいます。

ハンドルされていない例外: System.MissingMethodException: メソッドが見つかりません:
'System.Decimal CommonLib.CalcTaxIncludedPrice(System.Decimal)

私が動作検証した際はソリューションごとリコンパイルしており、プロジェクトBもリコンパイルされていたため、エラーに気づくことができませんでした。

このようにメソッドにデフォルト引数を追加する場合は、そのメソッドの呼び出し側がリコンパイルされるかどうか注意する必要があります。

どのように解決するのか

必ずリコンパイルされるのであれば問題ないのですが、今回のように呼び出し側の一部はリコンパイルしないという場合はメソッドをオーバーロードして機能を追加します。
元のメソッドはオーバーロードしたメソッドを呼び出すようにして処理を一箇所にまとめましょう。

CommonLib.cs
public static class CommonLib
{
    // オーバーロードして利率を渡せるバージョンのメソッドを作る
    public static decimal CalcTaxIncludedPrice(decimal price, decimal rate)
    {
        return price * rate;
    }

    // 元のメソッドはオーバーロードしたメソッドを呼び出すようにする
    public static decimal CalcTaxIncludedPrice(decimal price)
    {
        return CalcTaxIncludedPrice(price, 1.08M);
    }
}

こうすれば、引数が2つのメソッドと引数が1つのメソッドが存在するため、修正していないプロジェクトをリコンパイルしなくても、エラーは起きなくなります。

まとめ

この記事では私の実務での体験をもとにデフォルト引数の追加時の注意点と解決方法をまとめました。
呼び出し側を修正せずに機能を追加できるため、デフォルト引数はとても便利ですが、修正していないコードもリコンパイルが必要になるということは理解しておかないと、上で述べたようにアプリケーションに予期しない影響が出てくることがあるため、注意しましょう。

それではまた。

TomoProg

参考

オプション引数・名前付き引数 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
【C#】デフォルト引数のバージョニング問題 - 滅入るんるん

19
7
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?