10

私は自分のコードでこの種のパターンを頻繁に使用しているようですが、それはもはや単純なAutopropertyではないことを知っています。

  public IList<BCSFilter> BCSFilters { get; set; }

私が使用しているコードは次のとおりです。

    private IList<BCSFilter> _BCSFilters;

    /// <summary>
    /// Gets or sets the BCS filters.
    /// </summary>
    /// <value>The BCS filters.</value>
    public IList<BCSFilter> BCSFilters
    {
        get
        {
            if (_BCSFilters == null)
            {
                _BCSFilters = new List<BCSFilter>();
            }

            return _BCSFilters;
        }
        set
        {
            _BCSFilters = value;
        }
    }

これは、MainClass.BCSFiltersを実行するだけで、消費するコードでリストをインスタンス化する必要がないようにするためです。これは「通常の」パターン\これを行う正しい方法ですか?

重複する質問が見つかりませんでした

4

10 に答える 10

33

これは私がよく使うテクニックです。これは、objectsプロパティが消費コード内で実際に使用されていない限り、List <>オブジェクトをインスタンス化しないため、メモリリソースの節約にも役立ちます。これは「遅延読み込み」手法を使用します。

また、リストした「遅延読み込み」手法はスレッドセーフではありません。プロパティへの複数の呼び出しが同時に発生した場合、プロパティを新しいList <>オブジェクトに設定する複数の呼び出しが発生し、その結果、既存のList値が新しい空のList<>オブジェクトで上書きされる可能性があります。Getアクセサーをスレッドセーフにするには、次のようにLockステートメントを使用する必要があります。

private IList<BCSFilter> _BCSFilters;

// Create out "key" to use for locking
private object _BCSFiltersLOCK = new Object();

/// <summary>
/// Gets or sets the BCS filters.
/// </summary>
/// <value>The BCS filters.</value>
public IList<BCSFilter> BCSFilters
{
    get
    {
        if (_BCSFilters == null)
        {
            // Lock the object before modifying it, so other
            // simultaneous calls don't step on each other
            lock(_BCSFiltersLOCK)
            {
                if (_BCSFilters == null)
                }
                    _BCSFilters = new List<BCSFilter>();
                }
            }
        }

        return _BCSFilters;
    }
    set
    {
        _BCSFilters = value;
    }
}

ただし、List <>オブジェクトを常にインスタンス化する必要がある場合は、オブジェクトコンストラクター内で作成し、代わりに自動プロパティを使用する方が少し簡単です。次のように:

public class MyObject
{
    public MyObject()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IList<BCSFilter> BCSFilters { get; set; }
}

さらに、「set」アクセサーをパブリックのままにすると、消費コードはプロパティをNullに設定できるようになり、他の消費コードを壊す可能性があります。したがって、消費するコードがプロパティ値をNullに設定できないようにするための優れた手法は、設定されたアクセサーをプライベートに設定することです。このような:

public IList<BCSFilter> BCSFilters { get; private set; }

関連する手法は、代わりにプロパティからIEnumerable<>オブジェクトを返すことです。これにより、いつでもオブジェクト内でList <>タイプを置き換えることができ、消費するコードは影響を受けません。IEnumerable <>を返すには、IEnumerable <>インターフェイスを実装しているため、プレーンなList<>オブジェクトを直接返すことができます。次のように:

public class MyObject
{
    public MyObject()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IEnumerable<BCSFilter> BCSFilters { get; set; }
}
于 2009-11-24T12:09:05.040 に答える
10

あなたのパターンは完全に合理的な遅延読み込みパターンですが、スレッドセーフではないことに注意してください。

2つのスレッドが初めてこのプロパティに非常に接近してアクセスした場合、nullチェックパターンは、1つのスレッドがnullと評価される条件を妨げませんが、リストを初期化する機会を得る前に、2番目のスレッドもnullと評価します。この状況では、両方がリストを初期化します。

さらに、プロパティが返されるまでに、1つのスレッドにリストのコピーが1つあり、2番目のスレッドに別のコピーがある可能性があります。

シングルスレッド環境では問題にはなりませんが、注意が必要なことは間違いありません。

于 2009-11-24T12:32:50.610 に答える
5

あなたが望むものである限り、それは正しいパターンです:

  • 外部コードがリスト全体を置き換えることを許可します(instance.BCSFilters = null)
  • 読み取り時にリストを魔法のように作成します。ただし、ユーザーが(セット内で)nullに設定することは許可しているが、nullのままにすることは許可していないため(後で取得すると空のリストが生成されるため)、そのままでは注意が必要です。

IListを読み取り専用モードで公開することもできます(必要に応じて遅延初期化を使用)。これにより、ユーザーはリスト自体を上書きすることなく、IList内のアイテムのみを追加または削除できます。割り当てが多い場合は、コピーが必要になる場合があります。

通常、私はIListメンバーにゲッターしか持っておらず、IEnumerableを公開したり、getでコピーを返したりすることもあります(ただし、特定のAddメソッドとRemoveメソッドを提供する必要があるため、YMMV)

于 2009-11-24T12:12:44.143 に答える
5

参考までに、すでに行っているのとまったく同じことを行うためのもう少し簡潔な方法は、次のようになります。

private IList<BCSFilter> _BCSFilters;

public IList<BCSFilter> BCSFilters
{
    get
    {
        return _BCSFilters ?? (_BCSFilters = new List<BCSFilter>());
    }
    set
    {
        _BCSFilters = value;
    }
}
于 2009-11-24T21:59:09.497 に答える
3

あなたのアプローチは怠惰な初期化バージョンです

public class xyz
{
    public xyz()
    {
        BCSFilters = new List<BCSFilter>();
    }

    public IList<BCSFilter> BCSFilters { get; set; }
}
于 2009-11-24T12:08:41.260 に答える
3

別のトリックがあります:)

.Net4からLazyを使用するには。

私はこれをMarkSeemannブログで見ました:

 public class Order
{
    public Order()
    {
        _customerInitializer = new Lazy<Customer>(() => new Customer());
    }

    // other properties

    private Lazy<Customer> _customerInitializer;
    public Customer Customer
    {
        get
        {
            return _customerInitializer.Value;
        }
    }

    public string PrintLabel()
    {
        string result = Customer.CompanyName; // ok to access Customer
        return result + "\n" + _customerInitializer.Value.Address; // ok to access via .Value
    }
}

「_customerInitializer」がnullになることはないため、使用するのとまったく同じであることに注意してください。そしてそれもスレッドセーフかもしれません!コンストラクターは、LazyThreadSafetyMode列挙型でオーバーロードを取得できます。 http://msdn.microsoft.com/en-us/library/system.threading.lazythreadsafetymode.aspx

于 2012-12-04T14:44:42.000 に答える
2

これは、LazyLoadパターンの例です。これは受け入れられているパターンであり、完全に有効です。

C#で自動プロパティを使用し、コンストラクターでプロパティにインスタンスを割り当てることができます。Lazy Loadパターンの利点は、呼び出されない限りプロパティを初期化しないことです。これは、初期化にコストがかかる状況で役立ちます。

構文がより簡潔で、初期化に費用がかかる場合を除いて入力が少ないため、コンストラクターの初期化を使用した自動プロパティを好む傾向があります。初期化に費用がかかる場合は、遅延読み込みが適切に機能します。

于 2009-11-24T12:13:29.900 に答える
1

ええ、それは完全に正常です;-)

このような怠惰な作成は珍しいことではなく、理にかなっています。唯一の注意点は、フィールドを参照する場合は注意が必要なことです。

編集:しかし、私はクリスと他の人たちと一緒に行かなければなりません:autoプロパティを使用してコンストラクターでコレクションを初期化する方が(はるかに)良いパターンです。

于 2009-11-24T12:09:38.200 に答える
0

私たちは職場でそのパターンを使用しています。消費するコードで発生する可能性のあるnull参照例外を回避し、消費するコードを単純に保つので便利です。

于 2009-11-24T12:08:33.927 に答える
0

それは大丈夫なパターンです。Autopropertiesは、プロパティを使用する非常に単純で、ほぼ間違いなく最も一般的なシナリオの省略形であり、一部のシナリオでは、それを使用するのは単に賢明ではありません。できることは、代わりにコンストラクターでBCSFiltersをインスタンス化することです。そうすれば、autopropertiesを使用でき、null参照の例外について心配する必要はありません。

于 2009-11-24T12:09:43.740 に答える