クラス

[C#] 自動実装プロパティの書き方と使い方を解説

自動実装プロパティは、C#でフィールドのゲッターとセッターを簡潔に定義するための構文です。

通常のプロパティと異なり、バックフィールドを明示的に定義する必要がありません。

基本的な書き方は public型プロパティ名 { get; set; } です。

例えば、public string Name { get; set; } と書くと、Nameプロパティのゲッターとセッターが自動的に生成されます。

読み取り専用にしたい場合は get; のみを定義し、初期値をコンストラクタやプロパティ初期化子で設定します。

自動実装プロパティとは

自動実装プロパティは、C#におけるプロパティの簡略化された記述方法です。

従来のプロパティでは、ゲッターとセッターを手動で定義し、バッキングフィールドを明示的に作成する必要がありました。

しかし、自動実装プロパティを使用することで、これらの手間を省くことができます。

具体的には、プロパティを定義する際に、バッキングフィールドを自動的に生成し、コードをシンプルに保つことが可能です。

これにより、可読性が向上し、開発効率が高まります。

自動実装プロパティは、C# 3.0以降で導入され、特にデータモデルやDTO(データ転送オブジェクト)を作成する際に非常に便利です。

自動実装プロパティの基本的な書き方

基本構文

自動実装プロパティの基本的な構文は非常にシンプルです。

以下のように、プロパティ名の前にアクセス修飾子を指定し、プロパティの型を記述します。

public class Person
{
    public string Name { get; set; }  // 自動実装プロパティ
}

この例では、Nameというプロパティが自動的に生成され、string型の値を持つことができます。

ゲッターとセッターの自動生成

自動実装プロパティでは、ゲッターとセッターが自動的に生成されます。

これにより、プロパティの値を取得したり設定したりするためのコードを書く必要がありません。

public class Person
{
    public string Name { get; set; }  // ゲッターとセッターが自動生成される
}
public class App
{
    public static void Main()
    {
        Person person = new Person();
        person.Name = "山田太郎";  // セッターを使用
        Console.WriteLine(person.Name);  // ゲッターを使用
    }
}
山田太郎

初期値の設定方法

自動実装プロパティでは、初期値を設定することも可能です。

プロパティの定義時に初期値を指定することで、オブジェクト生成時に自動的にその値が設定されます。

public class Person
{
    public string Name { get; set; } = "無名";  // 初期値を設定
}
public class App
{
    public static void Main()
    {
        Person person = new Person();
        Console.WriteLine(person.Name);  // 初期値が表示される
    }
}
無名

読み取り専用プロパティの定義

読み取り専用プロパティは、セッターを省略することで定義できます。

この場合、プロパティの値はコンストラクタなどで設定する必要があります。

public class Person
{
    public string Name { get; }  // 読み取り専用プロパティ
    public Person(string name)
    {
        Name = name;  // コンストラクタで値を設定
    }
}
public class App
{
    public static void Main()
    {
        Person person = new Person("佐藤花子");
        Console.WriteLine(person.Name);  // 読み取り専用プロパティの値を表示
    }
}
佐藤花子

書き込み専用プロパティの定義

書き込み専用プロパティは、ゲッターを省略することで定義できます。

この場合、プロパティの値は外部から取得できません。

public class Person
{
    public string Name { private get; set; }  // 書き込み専用プロパティ
    public Person(string name)
    {
        Name = name;  // コンストラクタで値を設定
    }
}
public class App
{

    public static void Main()
    {
        Person person = new Person("鈴木一郎");
        // Console.WriteLine(person.Name);  // エラー: ゲッターがないためアクセスできない
    }
}

このように、書き込み専用プロパティは外部からの読み取りを防ぐことができます。

自動実装プロパティの使い方

クラス内での使用例

自動実装プロパティは、クラス内で簡単に使用できます。

以下の例では、Personクラスに自動実装プロパティを定義し、オブジェクトを生成してプロパティにアクセスしています。

public class Person
{
    public string Name { get; set; }  // 自動実装プロパティ
    public int Age { get; set; }       // 自動実装プロパティ
}
public class App
{

    public static void Main()
    {
        Person person = new Person();
        person.Name = "田中太郎";  // プロパティに値を設定
        person.Age = 30;           // プロパティに値を設定
        Console.WriteLine($"名前: {person.Name}, 年齢: {person.Age}");  // プロパティの値を表示
    }
}
名前: 田中太郎, 年齢: 30

コンストラクタとの併用

自動実装プロパティは、コンストラクタと併用することで、オブジェクト生成時にプロパティに初期値を設定できます。

以下の例では、コンストラクタを使用してNameAgeを設定しています。

public class Person
{
    public string Name { get; set; }  // 自動実装プロパティ
    public int Age { get; set; }       // 自動実装プロパティ
    public Person(string name, int age)  // コンストラクタ
    {
        Name = name;  // プロパティに値を設定
        Age = age;    // プロパティに値を設定
    }
}
public class App
{

    public static void Main()
    {
        Person person = new Person("佐藤花子", 25);  // コンストラクタで初期化
        Console.WriteLine($"名前: {person.Name}, 年齢: {person.Age}");  // プロパティの値を表示
    }
}
名前: 佐藤花子, 年齢: 25

プロパティ初期化子の使用

C# 6.0以降では、プロパティ初期化子を使用して、プロパティに初期値を直接設定することができます。

これにより、コンストラクタを使わずに初期値を設定できます。

public class Person
{
    public string Name { get; set; } = "無名";  // プロパティ初期化子
    public int Age { get; set; } = 0;            // プロパティ初期化子
}
public class App
{

    public static void Main()
    {
        Person person = new Person();  // コンストラクタはデフォルト
        Console.WriteLine($"名前: {person.Name}, 年齢: {person.Age}");  // 初期値を表示
    }
}
名前: 無名, 年齢: 0

デフォルト値の設定

自動実装プロパティでは、デフォルト値を設定することができます。

これにより、オブジェクト生成時に自動的にその値が設定されます。

以下の例では、NameAgeにデフォルト値を設定しています。

public class Person
{
    public string Name { get; set; } = "無名";  // デフォルト値
    public int Age { get; set; } = 18;           // デフォルト値
}
public class App
{

    public static void Main()
    {
        Person person = new Person();  // デフォルト値が適用される
        Console.WriteLine($"名前: {person.Name}, 年齢: {person.Age}");  // デフォルト値を表示
    }
}
名前: 無名, 年齢: 18

プロパティの変更通知との連携

自動実装プロパティを使用する際、プロパティの変更通知を行うことも可能です。

INotifyPropertyChangedインターフェースを実装することで、プロパティの値が変更された際に通知を行うことができます。

以下の例では、Nameプロパティの変更通知を実装しています。

using System.ComponentModel;
public class Person : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            if (name != value)
            {
                name = value;
                OnPropertyChanged("Name");  // 変更通知を発行
            }
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class App
{

    public static void Main()
    {
        Person person = new Person();
        person.PropertyChanged += (sender, e) =>
        {
            Console.WriteLine($"{e.PropertyName}が変更されました。");
        };

        person.Name = "鈴木一郎";  // プロパティの値を変更
    }
}
Nameが変更されました。

このように、自動実装プロパティは変更通知と連携させることで、データバインディングやMVVMパターンでの利用が容易になります。

自動実装プロパティの応用

プライベートセッターの使用

自動実装プロパティでは、セッターをプライベートにすることで、外部からの変更を制限することができます。

これにより、クラス内部でのみプロパティの値を設定できるようになります。

以下の例では、Nameプロパティのセッターをプライベートにしています。

public class Person
{
    public string Name { get; private set; }  // プライベートセッター
    public Person(string name)
    {
        Name = name;  // コンストラクタで値を設定
    }
}
public class App
{

    public static void Main()
    {
        Person person = new Person("山田太郎");
        // person.Name = "佐藤花子";  // エラー: セッターがプライベートのためアクセスできない
        Console.WriteLine(person.Name);  // 値を表示
    }
}
山田太郎

バッキングフィールドのカプセル化

自動実装プロパティを使用しつつ、バッキングフィールドを明示的に定義することで、プロパティのカプセル化を行うことができます。

これにより、プロパティの値に対する追加のロジックを実装することが可能です。

public class Person
{
    private string name;  // バッキングフィールド
    public string Name
    {
        get { return name; }
        set
        {
            if (!string.IsNullOrEmpty(value))  // バリデーション
            {
                name = value;
            }
        }
    }
}
public class App
{

    public static void Main()
    {
        Person person = new Person();
        person.Name = "鈴木一郎";  // 値を設定
        Console.WriteLine(person.Name);  // 値を表示
    }
}
鈴木一郎

プロパティのバリデーション

プロパティにバリデーションを追加することで、無効な値が設定されるのを防ぐことができます。

以下の例では、Ageプロパティに対して、0以上の値のみを受け入れるようにしています。

public class Person
{
    private int age;  // バッキングフィールド
    public int Age
    {
        get { return age; }
        set
        {
            if (value >= 0)  // バリデーション
            {
                age = value;
            }
            else
            {
                throw new ArgumentException("年齢は0以上でなければなりません。");
            }
        }
    }
}
public class App
{

    public static void Main()
    {
        Person person = new Person();
        person.Age = 25;  // 有効な値を設定
        Console.WriteLine(person.Age);  // 値を表示
        // person.Age = -5;  // エラー: 年齢は0以上でなければなりません。
    }
}
25

インターフェースとの組み合わせ

自動実装プロパティは、インターフェースと組み合わせて使用することができます。

これにより、異なるクラスで同じプロパティを実装することが容易になります。

以下の例では、IPersonインターフェースを定義し、Personクラスで実装しています。

public interface IPerson
{
    string Name { get; set; }  // インターフェースのプロパティ
}
public class Person : IPerson
{
    public string Name { get; set; }  // 自動実装プロパティ
}
public class App
{

    public static void Main()
    {
        IPerson person = new Person { Name = "田中太郎" };  // インターフェースを通じて設定
        Console.WriteLine(person.Name);  // 値を表示
    }
}
田中太郎

継承クラスでのプロパティのオーバーライド

自動実装プロパティは、継承クラスでオーバーライドすることも可能です。

これにより、基底クラスのプロパティの動作を変更することができます。

以下の例では、Personクラスを継承したEmployeeクラスNameプロパティをオーバーライドしています。

public class Person
{
    public virtual string Name { get; set; }  // 基底クラスのプロパティ
}
public class Employee : Person
{
    private string name;  // バッキングフィールド
    public override string Name  // オーバーライド
    {
        get { return name; }
        set { name = $"社員: {value}"; }  // 値を変更して設定
    }
}
public class App
{

    public static void Main()
    {
        Employee employee = new Employee();
        employee.Name = "佐藤花子";  // プロパティに値を設定
        Console.WriteLine(employee.Name);  // オーバーライドされたプロパティの値を表示
    }
}
社員: 佐藤花子

このように、自動実装プロパティはさまざまな応用が可能であり、柔軟な設計を実現するための強力なツールとなります。

自動実装プロパティの制約と注意点

バッキングフィールドへのアクセス

自動実装プロパティを使用すると、C#が内部でバッキングフィールドを自動生成しますが、開発者はそのフィールドに直接アクセスすることはできません。

これは、プロパティのカプセル化を促進する一方で、特定のロジックを実装する際に制約となることがあります。

バッキングフィールドにアクセスしたい場合は、手動でプロパティを定義する必要があります。

public class Person
{
    private string name;  // バッキングフィールド
    public string Name
    {
        get { return name; }
        set { name = value; }  // バッキングフィールドにアクセス
    }
}

プロパティの初期化タイミング

自動実装プロパティの初期化は、オブジェクトが生成されたときに行われますが、プロパティの初期化タイミングに注意が必要です。

特に、コンストラクタ内で他のプロパティを使用して初期化する場合、期待通りに動作しないことがあります。

以下の例では、NameプロパティをAgeプロパティの値に基づいて初期化しようとしていますが、Ageがまだ設定されていないため、意図した結果になりません。

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person()
    {
        Name = Age > 18 ? "成人" : "未成年";  // 初期化タイミングに注意
    }
}

スレッドセーフなプロパティの実装

自動実装プロパティは、スレッドセーフではありません。

複数のスレッドが同時にプロパティにアクセスし、値を変更する場合、データ競合が発生する可能性があります。

スレッドセーフな実装を行うためには、ロックを使用するか、Interlockedクラスを利用して、プロパティのアクセスを制御する必要があります。

public class ThreadSafePerson
{
    private string name;  // バッキングフィールド
    private readonly object lockObject = new object();  // ロックオブジェクト
    public string Name
    {
        get
        {
            lock (lockObject)  // ロックを使用
            {
                return name;
            }
        }
        set
        {
            lock (lockObject)  // ロックを使用
            {
                name = value;
            }
        }
    }
}

パフォーマンスへの影響

自動実装プロパティは、通常のプロパティに比べてパフォーマンスに大きな影響を与えることはありませんが、特に大量のデータを扱う場合や、頻繁にプロパティにアクセスする場合には注意が必要です。

プロパティのゲッターやセッターに複雑なロジックを追加すると、パフォーマンスが低下する可能性があります。

シンプルなプロパティを使用することが推奨されます。

データバインディングとの相性

自動実装プロパティは、データバインディングと非常に相性が良いですが、変更通知を行うためにはINotifyPropertyChangedインターフェースを実装する必要があります。

自動実装プロパティを使用する場合、プロパティの変更を通知するためのロジックを追加することが重要です。

以下の例では、Nameプロパティに変更通知を追加しています。

using System.ComponentModel;
public class Person : INotifyPropertyChanged
{
    private string name;  // バッキングフィールド
    public string Name
    {
        get { return name; }
        set
        {
            if (name != value)
            {
                name = value;
                OnPropertyChanged("Name");  // 変更通知を発行
            }
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

このように、自動実装プロパティを使用する際には、いくつかの制約や注意点が存在しますが、適切に利用することで、コードの可読性や保守性を向上させることができます。

C# 6.0以降の新機能と自動実装プロパティ

C# 6.0でのプロパティ初期化子

C# 6.0では、プロパティ初期化子が導入され、プロパティの定義時に初期値を直接設定できるようになりました。

これにより、コンストラクタを使用せずにプロパティにデフォルト値を簡単に設定できるようになり、コードがよりシンプルになります。

以下の例では、NameAgeプロパティに初期値を設定しています。

public class Person
{
    public string Name { get; set; } = "無名";  // プロパティ初期化子
    public int Age { get; set; } = 0;            // プロパティ初期化子
}
public class App
{

    public static void Main()
    {
        Person person = new Person();  // コンストラクタはデフォルト
        Console.WriteLine($"名前: {person.Name}, 年齢: {person.Age}");  // 初期値を表示
    }
}
名前: 無名, 年齢: 0

C# 7.0でのタプルと自動実装プロパティ

C# 7.0では、タプルが導入され、複数の値を簡単に返すことができるようになりました。

自動実装プロパティと組み合わせることで、クラスのインスタンスから複数のプロパティを一度に取得することができます。

以下の例では、GetPersonInfoメソッドがタプルを使用してNameAgeを返しています。

public class Person
{
    public string Name { get; set; } = "無名";
    public int Age { get; set; } = 0;
    public (string Name, int Age) GetPersonInfo()  // タプルを返すメソッド
    {
        return (Name, Age);
    }
}
public class App
{

    public static void Main()
    {
        Person person = new Person { Name = "田中太郎", Age = 30 };
        var info = person.GetPersonInfo();  // タプルを取得
        Console.WriteLine($"名前: {info.Name}, 年齢: {info.Age}");  // タプルの値を表示
    }
}
名前: 田中太郎, 年齢: 30

C# 8.0での読み取り専用プロパティの強化

C# 8.0では、読み取り専用プロパティが強化され、initアクセサが導入されました。

これにより、オブジェクトの初期化時にのみプロパティの値を設定できるようになり、後から変更できないプロパティを簡単に定義できます。

以下の例では、Nameプロパティがinitアクセサを使用しています。

public class Person
{
    public string Name { get; init; }  // 読み取り専用プロパティ
}
public class App
{

    public static void Main()
    {
        Person person = new Person { Name = "鈴木一郎" };  // 初期化時に設定
                                                       // person.Name = "田中太郎";  // エラー: 読み取り専用のため変更できない
        Console.WriteLine(person.Name);  // 値を表示
    }
}
鈴木一郎

C# 9.0でのレコード型と自動実装プロパティ

C# 9.0では、レコード型が導入され、データの不変性を簡単に実現できるようになりました。

レコード型は自動実装プロパティを使用して、プロパティの定義を簡素化し、データの比較やコピーを容易にします。

以下の例では、Personレコード型を定義しています。

public record Person
{
    public string Name { get; init; }  // 自動実装プロパティ
    public int Age { get; init; }       // 自動実装プロパティ
}
public class App
{

    public static void Main()
    {
        Person person1 = new Person { Name = "田中太郎", Age = 30 };
        Person person2 = person1 with { Age = 31 };  // 新しいインスタンスを作成
        Console.WriteLine($"名前: {person1.Name}, 年齢: {person1.Age}");  // person1の値を表示
        Console.WriteLine($"名前: {person2.Name}, 年齢: {person2.Age}");  // person2の値を表示
    }
}
名前: 田中太郎, 年齢: 30
名前: 田中太郎, 年齢: 31

このように、C# 6.0以降の新機能は、自動実装プロパティの使い方をさらに便利にし、コードの可読性や保守性を向上させるための強力なツールを提供しています。

自動実装プロパティの実践例

シンプルなクラスでの使用例

自動実装プロパティは、シンプルなクラスでの使用に非常に適しています。

以下の例では、Personクラスを定義し、NameAgeのプロパティを自動実装プロパティとして使用しています。

public class Person
{
    public string Name { get; set; }  // 自動実装プロパティ
    public int Age { get; set; }       // 自動実装プロパティ
}
public class App
{

    public static void Main()
    {
        Person person = new Person
        {
            Name = "山田太郎",
            Age = 28
        };

        Console.WriteLine($"名前: {person.Name}, 年齢: {person.Age}");  // プロパティの値を表示
    }
}
名前: 山田太郎, 年齢: 28

データモデルクラスでの使用例

データモデルクラスでは、自動実装プロパティを使用して、データの構造を簡潔に表現できます。

以下の例では、Productクラスを定義し、商品名と価格をプロパティとして持っています。

public class Product
{
    public string ProductName { get; set; }  // 自動実装プロパティ
    public decimal Price { get; set; }        // 自動実装プロパティ
}
public class App
{

    public static void Main()
    {
        Product product = new Product
        {
            ProductName = "ノートパソコン",
            Price = 120000m
        };

        Console.WriteLine($"商品名: {product.ProductName}, 価格: {product.Price}円");  // プロパティの値を表示
    }
}
商品名: ノートパソコン, 価格: 120000円

MVVMパターンでの使用例

MVVM(Model-View-ViewModel)パターンでは、データバインディングを利用してUIとデータを連携させます。

自動実装プロパティを使用することで、ViewModelのプロパティを簡潔に定義できます。

以下の例では、PersonViewModelクラスを定義しています。

using System.ComponentModel;
public class PersonViewModel : INotifyPropertyChanged
{
    private string name;  // バッキングフィールド
    public string Name
    {
        get { return name; }
        set
        {
            if (name != value)
            {
                name = value;
                OnPropertyChanged("Name");  // 変更通知を発行
            }
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class App
{

    public static void Main()
    {
        PersonViewModel viewModel = new PersonViewModel();
        viewModel.Name = "佐藤花子";  // プロパティに値を設定
        Console.WriteLine($"名前: {viewModel.Name}");  // プロパティの値を表示
    }
}
名前: 佐藤花子

エンティティフレームワークとの連携

自動実装プロパティは、エンティティフレームワーク(EF)と連携してデータベースのエンティティを定義する際にも便利です。

以下の例では、Customerクラスを定義し、EFのエンティティとして使用しています。

public class Customer
{
    public int Id { get; set; }  // 自動実装プロパティ
    public string Name { get; set; }  // 自動実装プロパティ
    public string Email { get; set; }  // 自動実装プロパティ
}
public class App
{

    public static void Main()
    {
        // DbContextを使用してデータベースに接続する例
        // var context = new YourDbContext();
        // context.Customers.Add(new Customer { Name = "田中太郎", Email = "tanaka@example.com" });
        // context.SaveChanges();
    }
}

ASP.NET Coreでの使用例

ASP.NET Coreアプリケーションでは、自動実装プロパティを使用してモデルを定義し、コントローラーやビューでデータを扱うことができます。

以下の例では、Userモデルを定義し、コントローラーで使用しています。

public class User
{
    public int Id { get; set; }  // 自動実装プロパティ
    public string Username { get; set; }  // 自動実装プロパティ
    public string Password { get; set; }  // 自動実装プロパティ
}
public class UserController : Controller
{
    public IActionResult Create()
    {
        User user = new User
        {
            Username = "admin",
            Password = "password123"
        };
        
        // ユーザーをデータベースに保存する処理
        // return View(user);
        return Ok($"ユーザー名: {user.Username}");  // プロパティの値を表示
    }
}
ユーザー名: admin

このように、自動実装プロパティはさまざまな実践的なシナリオで利用され、コードの可読性や保守性を向上させるための強力なツールとなります。

まとめ

この記事では、自動実装プロパティの基本的な概念から、具体的な使い方や応用例、C#の新機能との関連性まで幅広く解説しました。

自動実装プロパティは、シンプルなプロパティの定義を可能にし、コードの可読性や保守性を向上させるための強力なツールです。

これを機に、自動実装プロパティを活用して、より効率的なC#プログラミングを実践してみてください。

関連記事

Back to top button