web-dev-qa-db-ja.com

クラス内の自動実装プロパティとバッキングフィールドの割り当ての違いは何ですか

自動実装プロパティの使用を開始する前に、コンストラクターでプロパティを割り当てるとき(プロパティread onlyを作成する可能性があるため)、プロパティのプライベートメンバーに直接割り当てる必要があると教えられました(通常は、プロパティ名の前にアンダースコアを付けた名前にします。例:_propertyName。)

自動実装プロパティを使用するように切り替えたとき、プライベートメンバーのみがプロパティのクラス内で参照されるようにすることを確立するまで、慣行を続けました。自動実装されたプライベートメンバーが「バッキングフィールド」と呼ばれていることを理解しています。

バッキングフィールドの有用性を考えると、完全に入力されるまではインテリセンスには見えないのが魅力的です。さらに、プロパティを自動実装すると、プロパティのクラス内のRead Onlyプロパティに割り当てることができるように見えることがわかりました。

.netフレームワークで発生した変更を考慮して、クラス内からプロパティを処理するための「正しい」方法とは何かを疑問視し始めています。それはやや主観的であると理解していますが、私がこれを行った方法には欠点があるのでしょうか?そのため、これがどのように機能するか、およびソフトウェアの設計への影響(マイナーではありますが)について、いくつか質問があります。

  1. プロパティのクラス内から自動実装プロパティを呼び出すと、GetおよびSetが呼び出されますか(「読み取り専用」に設定されている場合でも)?
  2. プロパティのバッキングフィールドの呼び出しはこれをバイパスしますか?
  3. バッキングフィールドの代わりにプロパティを使用することで、プロセッササイクルを無駄にしますか(わずかですが)?
  4. それとも、バッキングフィールドを使用することで、将来的に潜在的な問題に身をさらすのですか?

またはより広い質問:

自動実装プロパティへの割り当てとバッキングフィールドへの直接割り当ての違いは何ですか?

[〜#〜]更新[〜#〜]

C#で自動実装されたバッキングフィールドにアクセスできないため、私の質問がvb.netに当てはまることを理解しました。また、C#でバッキングフィールドに直接割り当てる場合は、自動実装を使用できません。

したがって、より広範な質問と実践の質問はどちらの言語にも適用できます。

VBの狭められた答えを拡大するために更新する

だから私はたくさんの有益なフィードバックを得ましたが、すべての回答で、4つの特定の質問すべてに直接対処したものはありません(おそらくそれらは最初は無関係であると思われるため)、私の状況では、自動実装プロパティのバッキングフィールドに割り当てています。これは、重要で破壊的なコードをバイパスしないことを意味します(自動実装された非表示のGetおよびSetにはロジックがありません)。

バッキングフィールドはPrivateであり、自動実装として(クラスを作成したときに)個人的に作成したプロパティのバッキングフィールドにのみ割り当てることができることを覚えておいてください。これは、別の合計方法です。上記の私の質問のいくつか:

自動実装プロパティのバッキングフィールドに直接割り当てる場合の問題は何ですか?

ありがとう!

1
Hawkeye

バッキングフィールドに直接割り当てると、確実に機能します。

バッキングフィールドをラップするプロパティを使用して割り当てた場合、それが機能するかどうかはわかりません。プロパティが派生クラスでオーバーライドされた場合、代わりにthatバージョンのプロパティが呼び出されます。また、基本クラスのコンストラクターは派生クラスのコンストラクターの前に呼び出されるため、完全に初期化されていないクラスのプロパティを呼び出すことになり、エラーが発生する可能性があります。

このようなエラーが発生する例を次に示します。

class Base
{
    public virtual string Foo { get; set; }

    public Base()
    {
        Foo = "Hello world!";
    }
}


class Derived : Base
{
    List<string> _history;

    public Derived() : base()
    {
        _history = new List<string>();
    }
    public override string Foo
    {
        get
        {
            return base.Foo;
        }
        set
        {
            _history.Add(value);
            base.Foo = value;
        }
    }
}

Derivedをインスタンス化すると、履歴リストがまだ初期化されていないため、null参照例外が発生します。これは、バッキングフィールドに直接割り当てた場合は発生しません。

DotNetFiddle の私の例を参照してください

4
John Wu

デザインの答え:

代入は、副作用のある関数呼び出しと考えることができます。バッキングフィールドに割り当てる場合、それは preconditions および postconditions がないことを意味します。正しい値を設定する責任は、値を設定するすべてのコードに与えられます。これらのコードが1つのクラスに属している場合でも。

そのバッキングフィールドの要件が変わる場合は、そのフィールドが設定するすべての場所を変更する必要があります。これは明らかに良くないので避けてください。したがって、可能な場合は常に自動実装プロパティを使用する必要があります。

「Rubyでの実用的なオブジェクト指向設計」の著者:

「変数を直接参照するのではなく、常にインスタンス変数をアクセサメソッドでラップする...」

「変数を、メソッドでラップすることにより、それらを定義するクラスからも隠します...」

guarantee一貫した結果を得る唯一の方法であるため、常にゲッター/セッターを使用する必要があります。

これは、ジッタに依存する、パフォーマンスへの影響が小さい可能性があります。コールを調べてインライン化することは自由です。

プロパティを呼び出したり、バッキング変数を直接設定したりする場合、重大な変更を見逃す可能性があります。

バッキングフィールドを持つC#プロパティがあり、それを自動プロップにすることを検討しますが、バッキングフィールドは削除しないでください。理想的には、これは完全に安全である必要がありますが、バッキングフィールドが施設の外で使用された場合、壊れます。

VBでは、反対の問題があります。自動プロパティを取得してロジックを追加すると、バッキングフィールドへの参照がそのロジックをバイパスします。

それがパフォーマンスのボトルネックであるという非常にありそうもないシナリオを無視する(仕様を満たさないことがすべてに勝ります)...

ロジックがあり、特定の状況でそれをバイパスする方法が必要な場合は、バッキングフィールドを使用する必要があります。プロパティを外部からのみ読み取り、内部で書き込み可能にする場合にも使用する必要があります(vbには自動プロパティのプライベートセッターがありません)。

1
jmoreno