連載:[完全版]究極のC#プログラミング

Chapter13 自動実装と自動定義

川俣 晶
2010/02/17

 本記事は、(株)技術評論社が発行する書籍『[完全版]究極のC#プログラミング ― 新スタイルによる実践的コーディング』から、許可を得て転載しています。
 同書籍は、もともと本フォーラムにて連載していた『C# 2.0入門』、『C# 3.0入門』の記事を整理統合し、加筆、修正されたものです。

手元でまとめて読みたい方は、ぜひ書店などにてお買い求めください。

 【注意】本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。

13.1 ラムダ式を使ったダーティテク―refの代役

 本題に入る前に、軽い話題として1つの問題解決事例を紹介しよう。実際にC# 3.0でプログラミング中に遭遇した問題と解決である。ポイントだけ抜き出したシンプルなコードで説明を行う。

 まず、リスト13.1のようなソースコードがあったとする。ポイントは、doSomethingメソッドの引数にrefキーワードが付いていて、参照を渡している点である。

using System;

static class SomeClass
{
  public static int A = 0;
}

class Program
{
  private static bool doSomething(ref int a)
  {
    return ++a == 1;
  }

  static void Main(string[] args)
  {
    if (doSomething(ref SomeClass.A))
    {
      Console.WriteLine(SomeClass.A);
    }
    // 出力:1
  }
}
リスト13.1 refキーワードを使ったサンプルプログラム

 ところが、SomeClassクラスの実装が変更され、「A」はフィールドからプロパティに変更された(リスト13.2参照)。

static class SomeClass
{
  private static int a = 0;

  public static int A
  {
    get { return a; }
    set { a = value; }
  }
}
リスト13.2 変更後のSomeClassクラス

 しかし、これではコンパイルができない。refパラメータとしてプロパティは渡せないからである。

 さて、この問題はどう解決すればよいだろうか?

 まず、そもそも外部のクラスのフィールドに対してrefパラメータを使うことはうかつであり、好ましいコードではないという点に留意しよう。これが通常の開発フェーズにあるプログラムであれば、もっとマシな設計に変更することで対処すべきだと思う。

 だが、筆者が遭遇した対象は、すでに開発フェーズを終了し、メインテナンスフェーズに入ったソースコードであった。そのため、大幅な設計変更はリスクが大きいため、変更は最小限にしたいと考えた。

 そこで、refパラメータで変数を渡すことをやめ、代わりに変数の値の読み出しと書き込みを行うデリゲート型を受け渡すようにした(リスト13.3参照)。

class Program
{
  private static bool doSomething(Func<int> getter, Action<int> setter)
  {
    setter( getter() + 1 );
    return getter() == 1;
  }

  static void Main(string[] args)
  {
    if (doSomething(
            () => SomeClass.A, (v) => { SomeClass.A = v; }))
    {
      Console.WriteLine(SomeClass.A);
    }
  }
}
リスト13.3 デリゲート型を受け取るdoSomethingメソッド

 これで、変更は、doSomethingメソッドとそれを呼び出すコードだけで済むようになり、ほかのコードと動作は完全に同等のままとなった。

 さて、このような“変数を渡す代わりに変数を読み書きするデリゲート型を渡す(あるいはラムダ式を渡す)”書き方は、その後も数回活用し、意外と有用であることに気づいた。理由は次のとおりである。

  • 変数以外が対象でも使える(ファイルやDOMツリーの特定のノードを読み書きするなど)
  • 型のミスマッチがあっても使える(整数型として受け渡すのに、実際に格納する変数が文字列型であっても、ラムダ式の中で変換してミスマッチを吸収できる)
  • リアルタイムに変化する変数値などに使うと、つねに最新値が取り出せる(非同期プログラミングや寿命の長いオブジェクトで使うと便利)
  • 複数の書き込み対象を引数で渡すことも容易

 おそらく、このようなテクニックはすでに世の中に存在し、名前も付いているだろうが、筆者は仮に「getter/setterによる参照パターン」と呼んで重宝している(ただし、乱用は禁物)。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter13 自動実装と自動定義
  1.13.1 ラムダ式を使ったダーティテク―refの代役
    2.13.2 自動実装プロパティ
    3.13.3 自動実装プロパティのアクセス制御
    4.13.4 読み出し専用、書き込み専用はない
    5.13.5 “名無し”のクラス―匿名型
    6.13.6 匿名型の等価性
    7.13.7 匿名型の簡易記法
    8.13.8 匿名型の使用目的
    9.13.9 オブジェクト初期化子
    10.13.10 オブジェクト初期化子の本質とは?
    11.13.11 コレクションはreadonlyでも初期化できる
    12.13.12 オブジェクト初期化子の使用例/練習問題
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間