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

Chapter13 自動実装と自動定義

川俣 晶
2010/02/17

13.12 オブジェクト初期化子の使用例

 最後に、さるプログラムのために書いたコードを、テーマに沿わない部分を大幅に削除、修正を行ったうえで、リスト13.26として紹介する。あまり良いプログラムだとは思っていないが、オブジェクト初期化子を使うということが何を意味するかが浮き彫りになっていると思うので、掲載することにした。

using System;
using System.Collections.Generic;
using System.Reflection;

// スキルのレベルを整数で保管するクラス
// (このクラスはデータ保存のためシリアライズされる)
public static class Flags
{
  public static int スチル撮影;
  public static int 動画編集;
}

// 1つの種類のスキルを扱うクラス
public class Skill
{
  public readonly string Name;
  public readonly string Description;
  private readonly Func<int> getter;
  private readonly Action<int> setter;

  public static implicit operator Skill(SkillTemplate t)
  {
    return new Skill(t);
  }

  public Skill(SkillTemplate t)
  {
    Name = t.Name;
    Description = t.Description;
    getter = t.getter;
    setter = t.setter;
  }
}

// スキルを初期化するテンプレート
public class SkillTemplate
{
  public string Name;
  public string Description;
  public Func<int> getter;
  public Action<int> setter;
}

// スキルの一覧
public static class Skills
{
  public static Skill スチル撮影 = new SkillTemplate()
  {
    Name = "スチル撮影",
    Description = "静止画撮影を行うスキル",
    getter = () => Flags.スチル撮影,
    setter = (v) => { Flags.スチル撮影 = v; },
  };

  public static Skill 動画編集 = new SkillTemplate()
  {
    Name = "動画編集",
    Description = "動画データの編集を行うスキル",
    getter = () => Flags.動画編集,
    setter = (v) => { Flags.動画編集 = v; },
  };

  public static Skill[] GetSkillList()
  {
    List<Skill> list = new List<Skill>();
    foreach (FieldInfo info in typeof(Skills).GetFields(
                    BindingFlags.Public | BindingFlags.Static))
    {
      Skill item = (Skill)info.GetValue(null);
      list.Add(item);
    }
    return list.ToArray();
  }
}
リスト13.26 オブジェクト初期化子の使用例

 このプログラムは「スチル撮影」といった個人の能力を「スキル」という形でモデル化してオブジェクトに格納している。スキルの具体的な内容はあらかじめ決まっていて不変なので、Skillオブジェクトはreadonlyのフィールドでのみ構成されている(変化するスキルの能力値は別のFlagsクラスに格納している)。

 ここで問題になるのは、オブジェクト初期化子はreadonlyのフィールドを初期化できないことである。そこで、Skillクラスとは別に、同等の情報を自由に読み書きできるSkillTemplateクラスを用意し、これを初期化する。そして、SkillTemplateクラスからSkillクラスへの暗黙的な変換(implicit operator Skill)を用意し、

Skill スチル撮影 = new SkillTemplate() { …… };

という異なる型の代入を可能としている。これにより、SkillTemplateクラスを用意しなければならなかった点を除けば、わりとすっきりとソースコードをまとめることができた*9

*9 しかし、このやり方に100%満足しているわけではない。もっと良いやり方はないんだろうか?

【Exercise】練習問題

 次のプログラムは、Targetを3回別個の値で初期化している。このプログラムはどのような文字列を出力するか? 正しいものを選べ。

using System;

class V
{
    public V(string s)
    {
        Console.Write(s);
    }
}

class A
{
    public V Target = new V("グー");
    public A()
    {
        Target = new V("チョキ");
    }
}

class Program
{
    static void Main(string[] args)
    {
        A a = new A() { Target = new V("パー") };
    }
}
  1. グーチョキパー
  2. チョキグーパー
  3. チョキパーグー
  4. チョキパーパー
  5. グーパーパー

 ◎解答:「1」(この行をマウスで選択してください)End of Article


 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 記事ランキング

本日 月間