ゲッターのみの自動プロパティ、設定できるのはなぜですか?


95

自動プロパティを作成しました:

public int Foo { get; } 

これはゲッターのみです。しかし、コンストラクターを作成するときに、値を変更できます。

public MyClass(string name)
{
    Foo = 5;
}

これが取得専用であるにもかかわらず、なぜそれが可能ですか?


実際にはセッターを使用しません(セッターがないため)。基礎となるフィールドを直接設定します(このフィールドは私たちには表示されないため、プロパティ名を使用する必要があります)
Dennis_E

14
初期化/設定できない場合、プロパティはどのように使用されますか?ヤコブマサドはそれに完全に答えました
Vikhram

回答:


122

これは新しいC#6機能である「Getter-only auto-properties」であり、このMSDNマガジンの記事「C#:The New and Improvementd C#6.0」で説明されている「読み取り専用プロパティの自動プロパティ初期化子」としても知られています。MichaelisおよびC#6.0ドラフト言語仕様

読み取り専用フィールドのセッターはコンストラクターでのみアクセスできます。他のすべてのシナリオでは、フィールドは引き続き読み取り専用であり、以前と同じように動作します。

これは、入力する必要があるコードの量を減らし、値を保持するプライベートモジュールレベルの変数を明示的に宣言する必要をなくすための便利な構文です。

この機能は、C#3の自動実装プロパティの導入以来、変更可能なプロパティ(ゲッターとセッターを持つもの)が不変なプロパティ(ゲッターのみを持つもの)よりも速く書けるようになり、人々が可変プロパティを使用して、通常は読み取り専用プロパティに必要なバッキングフィールドのコードを入力しなくても済むようにしたいと考えています。自動実装プロパティの詳細については、Microsoft C#プログラミングガイドの関連セクションを参照してください

Sean Sextonによるこのブログ投稿「#1,207 – C#6.0 –読み取り専用プロパティの自動プロパティ初期化子」には、次のような適切な説明と例があります。

C#6.0より前では、読み取り専用(不変)プロパティが必要な場合は、通常、以下に示すように、コンストラクターで初期化される読み取り専用のバッキングフィールドを使用していました。

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

    // DogCreationTime is immutable
    private readonly DateTime creTime;
    public DateTime DogCreationTime 
    {
        get { return creTime; }
    }

    public Dog(string name)
    {
        Name = name;
        creTime = DateTime.Now;
    }
}

C#6.0では、自動実装プロパティを使用して、読み取り専用プロパティを実装できます。これを行うには、自動プロパティ初期化子を使用します。結果は、バッキングフィールドを明示的に宣言する必要がある上記の例よりもはるかにクリーンです。

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

    // DogCreationTime is immutable
    public DateTime DogCreationTime { get; } = DateTime.Now;

    public Dog(string name)
    {
        Name = name;
    }
}

詳細については、GitHubのドットネットRoslynリポジトリでも確認できます

自動プロパティは、セッターなしで宣言できるようになりました。

ゲッターのみの自動プロパティのバッキングフィールドは、暗黙的に読み取り専用として宣言されます(ただし、これはリフレクションの目的でのみ重要です)。上記の例のように、プロパティの初期化子を介して初期化できます。また、getter-onlyプロパティを宣言型のコンストラクタ本体で割り当てることができます。これにより、値が基になるフィールドに直接割り当てられます。

これは、型をより簡潔に表現することですが、変更可能な型と不変の型の間の言語の重要な違いも取り除かれていることに注意してください。それは素晴らしかった。現在、ゲッターのみの自動プロパティを使用することで、プレイフィールドが変更可能と不変の間で平準化されました。

そして、でC#6.0のドラフト言語仕様(NBは:これまでマイクロソフトなどが懸念しているとして、言語仕様は最終的なもので、それはあるまだEMCA / ISOの規格として承認されるため、「ドラフト」):

自動的に実装されたプロパティ

自動的に実装されるプロパティ(または、略して自動プロパティ)は、セミコロンのみのアクセサ本体を持つ非抽象の非外部プロパティです。自動プロパティにはgetアクセサーが必要で、オプションでsetアクセサーを含めることができます。

プロパティが自動的に実装されるプロパティとして指定されている場合、非表示のバッキングフィールドがプロパティで自動的に使用可能になり、アクセサーはそのバッキングフィールドに対して読み取りと書き込みを行うように実装されます。自動プロパティにsetアクセサーがない場合、バッキングフィールドは読み取り専用と見なされます(読み取り専用フィールド)。readonlyフィールドと同様に、getter-only自動プロパティも、それを囲むクラスのコンストラクターの本体に割り当てることができます。このような割り当ては、プロパティの読み取り専用バッキングフィールドに直接割り当てられます。

自動プロパティには、オプションでproperty_initializerを含めることができます。これは、variable_initializer(変数初期化子)としてバッキングフィールドに直接適用されます。


1
これはバカなデザインです。プロパティが読み取り専用と見なされる場所に設定されている場合、これはコンパイル時エラーになります。
Shiv

私にとって奇妙なのはCS0200 C# Property or indexer cannot be assigned to -- it is read only、通常のプロパティを使用するときに、コンパイラエラーが表示されることです。「Getter-only auto-properties」は読み取り専用と見なされますか?
カイルデラニー

24

これはC#6の新機能で、読み取り専用プロパティを作成し、コンストラクターから(または宣言時にインラインで)その値を初期化できます。

このプロパティの値をコンストラクタの外で変更しようとすると、コンパイルエラーが発生します。

値を初期化すると(インラインまたはコンストラクター内で)、その値を変更できないという意味で読み取り専用です。


それで、説明は何ですか?getterの定義は?
Noam B.

16

コンストラクター(または自動プロパティ初期化子)から読み取り専用プロパティを初期化できない場合、そのタイプのデフォルト値(数値の場合は0、参照タイプの場合はnull)を常に返すため、このプロパティは役に立ちません。 )。すべてのC#バージョンの読み取り専用フィールドに同じセマンティクスが適用されました。

真のゲッターのみのプロパティ(コンストラクターから初期化できない)を定義するには、定義の一部として返すものを指定する必要があります。

public int Foo { get { return 5; } }

または、C#6でより簡潔に:

public int Foo => 5;

完全に真実ではありません。読み取り専用プロパティは、コード内のいくつかの条件をカプセル化するのに非常に役立ちます。ifステートメントとほとんどすべてのコードを記述して、他のプロパティまたは条件を評価し、プロパティを読み取るたびに適切な値を返すことができます
sebagomez

3
@sebagomez:私はあなたのポイントを理解しているのかわかりません-私の例ではそれを示していませんか?
ダグラス

5

「自動的に実装される読み取り専用プロパティ」

まず最初に、このプロパティが

public string FirstName { get; }

「自動的に実装される読み取り専用プロパティ」として知られています

これを確認するには、Visual Studioで上記のコードを実行して確認します。言語バージョンをC#6.0からC#​​5.0に変更すると、コンパイラーは次の例外をスローします 。C#5では、機能「読み取り専用の自動実装プロパティ」は使用できません。言語バージョン6以降を使用してください。

C#言語バージョンを変更するには、こちらにアクセスしてください

今、私はあなたの2番目の質問に来ています

「これはゲッターのみです。しかし、コンストラクターを構築すると、値を変更できます」

マイクロソフトは、読み取り専用のロジックに「読み取り専用の自動実装プロパティ」を導入しました。私たちが知っているように、キーワード「readonly」はC#1.0から利用可能です。我々は、フィールド上の修飾子として「読み取り専用」のキーワードを使用し、そのフィールドが中に割り当てることができる2つの方法のいずれかの宣言時同じクラスのコンストラクタで。

同様に、「読み取り専用の自動実装プロパティ」の値は2つの方法で割り当てることができます

Way1(宣言時):

public string FirstName { get; } = "Banketeshvar";

Way2(同じクラスのコンストラクター内)

Person()
{
 FirstName  = "Banketeshvar";
}

純粋に読み取り専用プロパティ

純粋に読み取り専用のプロパティを探している場合は、これを行ってください

public string FullName => "Manish Sharma";

現在、コンストラクタから「FullName」プロパティの値を割り当てることはできません。そうしようとすると、次の例外がスローされます

「プロパティまたはインデクサー 'Person.FullName'を割り当てることはできません-読み取り専用です」


2
FullName => "foo bar"は毎回評価されることに注意してください。
juFo 2019年

4

自動プロパティ機能は、C#3.0リリース中に言語に追加されました。これにより、バッキングフィールドなしでプロパティを定義できますが、これらの自動プロパティをデフォルト以外の値に初期化するには、コンストラクタを使用する必要があります。C#6.0には、自動プロパティ初期化子と呼ばれる新機能が導入されています。これにより、以下のようなコンストラクターなしでこれらのプロパティを初期化できます。

以前は、自動プロパティを使用してオブジェクトを作成し、自動プロパティを以下のようなデフォルト以外の値に初期化する場合は、コンストラクタが必要です。

public class MyClass
{
    public int Foo { get; }

    public Foo(int foo)
    {
        Foo = foo;
    }
}

C#6.0では、自動プロパティで初期化子を使用できるため、明示的なコンストラクタコードは不要です。

public string Foo { get; } = "SomeString";

public List<string> Genres { get; } = new List<string> { "Comedy", "Drama" };

この詳細については、こちらをご覧ください。


1

宣言readonlyされた変数はコンストラクター内で書き込むことができますが、属性を尊重する言語では、コンストラクターが戻った後は変更できません。この修飾子は言語機能として提供されました。コンストラクターパラメーターに基づいて値が変化するフィールド(コンストラクターの開始前に初期化できない)が必要になることがよくありますが、コンストラクターが戻った後に変更する必要はないためです。フィールドとして公開されている変数にのみ使用できます。readonly-qualifiedフィールドのセマンティクスは、多くの場合、パブリックメンバーにとっては完璧です。ただし、クラスでは、メンバーではなく、不変のものでも、フィールドではなくプロパティとして公開する方がよい場合が多くあります。

クラスが通常のフィールドと同じくらい簡単に可変プロパティを公開できるように、読み取り/書き込み自動プロパティが存在するのと同様に、クラスがreadonly-qualifiedフィールドと同じくらい簡単に不変プロパティを公開できるようにする読み取り専用自動プロパティが存在します。readonly-qualifiedフィールドをコンストラクターで書き込むことができるのと同じように、get-onlyプロパティでも同様です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.