MENU

.NETのプロパティ(Property)とは

Propertyってなんだ
目次

始めに

C#を書いていると、こんなのに出くわします。

public int Id {get; set;}

getterとsetterを省略して書けるんだ、でも、publicでいいのか?なんて考えながらとりあえずそのまま書いています。そのままでも不都合があるわけではないのですが、ちょっと気になって調べてみました。

Propertyとは

Propertyはprivateのフィールドを読み書き、計算するための柔軟なメカニズムを提供します。Propertyは、accessorsと呼ばれる特別なメソッドを用いることによって、publicなメンバのように使用することができます。この特徴は、データへの手軽なアクセスやメソッドの安全性・柔軟性を促進します。

microsoft, Properties(C# Programming Guide), 更新日 2024/03/15

privateのフィールドに対する読み書きの手段が、Propertyです。そのため、上にも書いた

public int Id{get; set;}

は、あくまで

private int _id

への読み書きの手段になります。しかし、毎回フィールドを定義する必要はなく、そのあたりはコンパイラが自動で用意してくれます。

Propertyは、accessorsと呼ばれるメソッドを用いることによって、Propertyをpublicなメンバのように使用することができます。accessorsは、3種類あります。

accessors

get

getはフィールドの値の読み込みを行います。ただ値を取得するのみではなく、logicを書いたり、expression body(=>)を使って定義することもできます。

// コンパイラがprivateのフィールドを自動で用意する
public String Name{get; set;} 

// logicを書いたもの
private String name;
public String Name{
    get{
        if(name == null){
            return "NoName";
        } 
    return name;
    };
    set;
}

// expression bodyを用いたもの
private String name;
public String Name => name;

set

setは、フィールドに対する書き込みの役割を担います。こちらもgetと同様にlogicを書くことができます。

// コンパイラがフィールドを自動で用意する書き方
public int id{get; set;}

// setterにlogicを用意することも可能
private String _id;
public String id{get; set{
    if(value.length > 6){
        throw new Exception("idは6文字以下にしてください");
    }
    _id = value;
}

// read-onlyも可能
private String _id;
public String id{get; private set;}

init

initのみのsetterは、propertyやindexer要素にコンストラクション時のみ値を詰めることができます。initは、propertyに不変性を与えることができるため、オブジェクトを初期化した後は変更することができません。

Microsoft, init(C# Reference). 最終更新日:2023/12/6, 拙訳

init accessorはpropertyに対して、コンストラクション時のみの値の書き込みを許可します。そのため、一度生成した後は値を変更することができません。では、get accessorのみのpropertyとどう違うのか。

internal class Student
{
    public Student(int id, String name)
    {                        
        Id = id;
        Name = name;
    }
    public int Id { get; }

    public String Name { get; init; }
   
}
public void initStudent()
{
    // OK
    Student student1 = new Student(id: 1, name: "Bob");

    // NG
    Student student2 = new Student()
    {
        id = 2,
        Name = "Mike"
    };

    // OK
    Student student3 = new Student(id: 3)
    {
        Name = "Jecika"
    };            
}

Studentクラスを例に、いくつかの生成方法を示しました。まず、student1は、コンストラクタで値をpropertyに書き込んでおり、これはコンパイルエラーになりません。student2では、object初期化子で値を書き込んでおり、get accessorのみのpropertyであるidはコンストラクタでのみ値の書き込みができるため、これはコンパイルエラーになります。そのため、student3のように、idはコンストラクタ、Nameはobject初期化子で定義するのであれば、可能です。

Expressionを用いたProperty

public class SaleItem
{
    string _name;
    decimal _cost;

    public SaleItem(string name, decimal cost)
    {
        _name = name;
        _cost = cost;
    }

    public string Name
    {
        get => _name;
        set => _name = value;
    }

    public decimal Price
    {
        get => _cost;
        set => _cost = value;
    }
}
microsoft, Properties(C# Programming Guide), 更新日 2024/03/15

こちらのクラスでは、_nameと_constを暗黙的にprivate変数として定義し、それぞれにアクセスするため、publicなフィールドとして、NameとPriceを用いています。また、NameとPriceのaccessor(getter, setter)では、lambda式を使用しております。

自動実装されたProperty

public class SaleItem
{
    public string Name
    { get; set; }

    public decimal Price
    { get; set; }
}
microsoft, Properties(C# Programming Guide), 更新日 2024/03/15

いくつかの事例では、propertyのget, set accessorsは、追加のlogicなしに、backing fieldへの値の割り当てと値の取得のみを行います。自動実装されたpropertiesを用いることによって、C#コンパイラがbacking fieldを明らかに提供している間、コードを単純化することができます。

microsoft, Properties(C# Programming Guide), 更新日 2024/03/15

propertyのいくつかの書き方では、privateなfield (backing field)はコンパイラによって自動で実装されるため、明示的に定義する必要はないようです。そのため、最初に話した以下のコードは、カブセル化の視点からは問題ないことになります。

public int id{get; set;}

では、いくつかの書き方ってなんだ、というところは、すみません、調べることができませんでした。実際に書きながら試していただけたらと思います。

まとめ

Propertyのaccessorと2つの書き方について調査しました。get・set・initはそれぞれ読み込み・書き込み・生成時のみの書き込みを意味しています。また、C#のコンパイラは、ある特定の書き方であれば、backing fieldを自動で生成してくれるため、明示的に書く手間を省いてくれます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

コメント

コメントする

CAPTCHA


目次