tag:crieit.net,2005:https://crieit.net/tags/%E5%BC%95%E6%95%B0%E5%90%8D/feed 「引数名」の記事 - Crieit Crieitでタグ「引数名」に投稿された最近の記事 2022-06-15T09:04:01+09:00 https://crieit.net/tags/%E5%BC%95%E6%95%B0%E5%90%8D/feed tag:crieit.net,2005:PublicArticle/18208 2022-06-02T19:00:09+09:00 2022-06-15T09:04:01+09:00 https://crieit.net/posts/dare-to-write-argument-names-patterns 【C#】あえて引数名を書くパターン <p>C# では、メソッドやコンストラクタに引数を渡す際に引数名を省略してコードを書く場合が多いと思いますが、明示的に引数名を書くこともできます。</p> <pre><code class="csharp">int Add(int x, int y) => x + y; // 引数名を省略する場合 var hoge = Add(10, 20); // 引数名を明記する場合 var fuga = Add(x: 10, y: 20); </code></pre> <p>省略しても問題ない引数名を、わざわざ明記するパターンは以下のような場合が考えられます。</p> <h2 id="メソッドで定義している引数の順番とは違う順番で書ける"><a href="#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%A7%E5%AE%9A%E7%BE%A9%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E5%BC%95%E6%95%B0%E3%81%AE%E9%A0%86%E7%95%AA%E3%81%A8%E3%81%AF%E9%81%95%E3%81%86%E9%A0%86%E7%95%AA%E3%81%A7%E6%9B%B8%E3%81%91%E3%82%8B">メソッドで定義している引数の順番とは違う順番で書ける</a></h2> <pre><code class="csharp">float Div(float x, float y) => x / y; // x と y をメソッド定義とは異なる順番で渡す Console.Write(Div(y: 2f, x: 1f)); // 0.5 </code></pre> <p>とはいえ、何か特別な意図がない限りこういった使い方はしないと思います。</p> <h2 id="複数の省略可能引数(オプション引数)のうち一つを明示的に指定する"><a href="#%E8%A4%87%E6%95%B0%E3%81%AE%E7%9C%81%E7%95%A5%E5%8F%AF%E8%83%BD%E5%BC%95%E6%95%B0%EF%BC%88%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E5%BC%95%E6%95%B0%EF%BC%89%E3%81%AE%E3%81%86%E3%81%A1%E4%B8%80%E3%81%A4%E3%82%92%E6%98%8E%E7%A4%BA%E7%9A%84%E3%81%AB%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B">複数の省略可能引数(オプション引数)のうち一つを明示的に指定する</a></h2> <pre><code class="csharp">void Demo(int x, bool isHoge = false, bool isFuga = false, bool isPiyo = false) { Console.WriteLine($"x: {x}"); Console.WriteLine($"isHoge: {isHoge}"); Console.WriteLine($"isFuga: {isFuga}"); Console.WriteLine($"isPiyo: {isPiyo}"); } // isHoge と isFuga はデフォルト値のまま、isPiyo のみ指定 Demo(10, isPiyo: true); // x: 10 // isHoge: False // isFuga: False // isPiyo: True // 名前付き引数を使用しない場合、すべての引数を明示的に指定する必要がある Demo(10, false, false, true); </code></pre> <p>これも、元々のメソッドの設計があまり良くないなどの理由で、後付けで省略可能引数がわんさか付いているようなメソッドというのは古いコードではよくあります。</p> <h2 id="可読性の向上;「この引数でどうなるんだっけ?」対策"><a href="#%E5%8F%AF%E8%AA%AD%E6%80%A7%E3%81%AE%E5%90%91%E4%B8%8A%EF%BC%9B%E3%80%8C%E3%81%93%E3%81%AE%E5%BC%95%E6%95%B0%E3%81%A7%E3%81%A9%E3%81%86%E3%81%AA%E3%82%8B%E3%82%93%E3%81%A0%E3%81%A3%E3%81%91%EF%BC%9F%E3%80%8D%E5%AF%BE%E7%AD%96">可読性の向上;「この引数でどうなるんだっけ?」対策</a></h2> <p>例として以下の <code>StreamWriter</code> の場合を考えてみます。</p> <pre><code class="csharp">var path = GetPath(); using (var sw = new StreamWriter(path, flase)) sw.Write("hoge"); </code></pre> <p><code>StreamWriter</code> のコンストラクタの第2引数に <code>false</code> を渡しています。この第2引数は <code>append</code> という引数名で定義されており、既にファイルが存在する場合に末尾に書き込むか、新しく上書きするかを真偽値で指定します<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p> <p>ところが数年ぶりに <code>StreamWriter</code> を使ってこのコードを見た場合、この <code>false</code> がどういう作用をもたらすのか<strong>瞬時に</strong>理解できるでしょうか?</p> <p>もちろん、IDE上で見る場合は引数の説明をいつでも参照することができます。そうでない場合でも、<a target="_blank" rel="nofollow noopener" href="https://referencesource.microsoft.com/">公式のリファレンスソース</a>を参照することができることでしょう。この <code>false</code> が何をもたらすかというのは、数十秒あれば解決できる疑問かもしれません。</p> <p>しかし、引数名を明記しておくと数十秒とかからず、<strong>コードを見ただけで瞬時に</strong>判断できるようになります。</p> <pre><code class="csharp">var path = GetPath(); using (var sw = new StreamWriter(path, append: flase)) sw.Write("hoge"); </code></pre> <p>「この <code>false</code> の引数名、<code>append</code> ってことはこの場合追記じゃなくて上書きなのか」と、<strong>定義やドキュメントを参照するまでもなく</strong>コードを読んだ瞬間に意味を理解することができます。これはかなり大きなメリットなのではないでしょうか。</p> <p>さらに、今回は <code>StreamWriter</code> を例に出しましたが、もしこれが公式のライブラリではなく、ドキュメントもろくにメンテされていない独自ライブラリだとしたらどうでしょう。「そのような環境の方が問題だろ!」というのももっともですが、しかし全てのドキュメントのメンテが行き届いている環境ばかりではありません。</p> <p>個人的には、コメントに「falseは上書き」みたいなことを書くよりは、<code>append: false</code> と書いた方が余程良いと思います<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p> <hr /> <p>以上、個人的に想定する「引数名を明示する理由」をまとめてみました。</p> <p>以下、おまけ。</p> <p>最後のコメント的な用法に関しては、自作クラスを使う場合は <code>WritingMode</code> みたいな <code>Enum</code> を受け取るようにして、</p> <pre><code class="csharp">using (var ow = OriginalWriter(path, WritingMode.Overwrite)) ow.Write("hoge"); // WritingMode は // WritingMode.Overwrite => 上書き // WritingMode.Append => 追記 </code></pre> <p>という形で受け取るようにすれば、引数名を書かずとも「<strong>この引数では書込みモードを指定してますよ</strong>」という意図が明確になることもあります。自作する場合は、たとえ真偽値で判断できる場合でもあえて <code>Enum</code> を併用して、より意図を込めやすくなるように工夫できれば良いと思います。</p> <div class="footnotes" role="doc-endnotes"> <hr /> <ol> <li id="fn:1" role="doc-endnote"> <p><strong>参考</strong> StreamWriter クラス|StreamWriter(String, Boolean)<br /> <a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/ja-jp/dotnet/api/system.io.streamwriter?view=net-6.0">https://docs.microsoft.com/ja-jp/dotnet/api/system.io.streamwriter?view=net-6.0</a> <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> <li id="fn:2" role="doc-endnote"> <p>コメントが悪という主張ではなく、<strong>コードで表現できることは、できるだけコードで表現すべき</strong>という主張です。たとえば「なぜ追記じゃなくて上書きをするのか」という意図が共有してあるのは良いコメントだと感じます。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> </ol> </div> あぱしょに