読者です 読者をやめる 読者になる 読者になる

1.21 jigowatts

Great Scott!

C-3PO、プロパティを変更されそうになる

概要

プロパティについてC-3POと帝国PGのやり取りにより理解を深めます。
f:id:sh_yoshida:20131016022827p:plain

ドロイド生産

まずC-3POを生産するためにドロイドクラスを用意します。
そして、ドロイドの型番をプロパティとして表現します。

class Droid
{
    string Model { get; set; }
}

早速起動してみましょう。

static void Main(string[] args)
{
    Droid droid = new Droid();
    droid.Model = "C-3PO";
    Console.WriteLine("ワタシハ、{0}デス。" , droid.Model);

    Console.ReadKey();
}

エラーです。
ドロイドクラスのプロパティにアクセシビリティを何も設定していないため、デフォルトprivateによりアクセスできません。
f:id:sh_yoshida:20131016014528p:plain

publicに変えましょう。これでエラーも消えました。

class Droid
{
    public string Model { get; set; }
}

ここで残念ながら帝国のPGによりC-3POのデータが書き換えられてしまいました。

static void Main(string[] args)
{
    Droid droid = new Droid();
    droid.Model = "C-3PO";
    Console.WriteLine("ワタシハ、{0}デス。" , droid.Model);
    droid.Model = "B1 battle droid";
    Console.WriteLine("ワタシハ、{0}デス。", droid.Model);
    Console.ReadKey();
}

f:id:sh_yoshida:20131016022827p:plain

private防御壁

アクセシビリティをpublicにすることでプロパティが書き換えられてしまったので、setアクセサをprivateにもどします。

class Droid
{
    public string Model 
    {
        get; 
        private set; 
    }
}

これだとまたsetアクセサでドロイドクラスのプロパティにアクセスできずにエラーとなってしまいます。

この場合、コンストラクタであればクラスのメンバなのでprivateなsetアクセサも利用できるので、コンストラクタによる初期化を行います。

class Droid
{
    public Droid(string model)
    {
        Model = model;
    }

    public string Model 
    {
        get; 
        private set; 
    }
}

これで外部からはプロパティをsetできません。

static void Main(string[] args)
{
    Droid droid = new Droid("C-3PO");
    //droid.Model = "C-3PO";
    Console.WriteLine("ワタシハ、{0}デス。" , droid.Model);
    //droid.Model = "B1 battle droid";
    Console.WriteLine("ワタシハ、{0}デス。", droid.Model);
    Console.ReadKey();
}

外部からは...

class Droid
{
    public Droid(string model)
    {
        Model = model;
    }

    public string Model 
    {
        get; 
        private set; 
    }

    public void hack()
    {
        Model = "B1 battle droid";
    }

}

また帝国のPGにデータが書き換えられてしまいました。

static void Main(string[] args)
{
    Droid droid = new Droid("C-3PO");
    Console.WriteLine("ワタシハ、{0}デス。" , droid.Model);
    droid.hack();
    Console.WriteLine("ワタシハ、{0}デス。", droid.Model);
    Console.ReadKey();
}

f:id:sh_yoshida:20131016022827p:plain

読み取り専用フィールド展開

思い切ってsetアクセサを取ってしまいます。
そして、明示的なバッキングフィールドをreadonlyで用意し、getアクセサはそれを返すようにします。

class Droid
{
    private readonly string _model;
    public Droid(string model)
    {
        _model = model;
    }

    public string Model 
    {
        get
        {
            return _model;
        }
        
    }

    public void hack()
    {
        Model = "B1 battle droid";
    }

}

この対応によりコンストラクタでのみプロパティが設定できるようになりました。
f:id:sh_yoshida:20131016024033p:plain
コンストラクタ以降の処理でプロパティを書き換えようとした場合、読み取り専用のため怒られます。
これで帝国の攻撃は凌げるのではないでしょうか。
f:id:sh_yoshida:20131016024311p:plain

参考

プログラミングC# 第6版

プログラミングC# 第6版