ガード句っぽい選択
あー本題からそれてるような気もしますが。
あと型名勝手に変えちゃいましたが。
class Generation
{
public static readonly Generation Over60 = new Generation(60, null);
public static readonly Generation Over50 = new Generation(50, 59);
public static readonly Generation Over40 = new Generation(40, 49);
public static readonly Generation Over30 = new Generation(30, 39);
public static readonly Generation Over20 = new Generation(20, 29);
public static readonly Generation Under20 = new Generation(null, 19);
public static Generation GetGeneration(int age)
{
return
new[]
{
Over60,
Over50,
Over40,
Over30,
Over20,
Under20,
}
.Single(item => item.IsIncludeing(age));
}
private Generation(int? lowerLimit, int? upperLimit)
{
LowerLimit = lowerLimit;
UpperLimit = upperLimit;
}
public readonly int? LowerLimit;
public readonly int? UpperLimit;
public bool IsIncludeing(int age)
{
bool isWithinLowerLimit = LowerLimit.HasValue ? (LowerLimit <= age) : true;
bool isWithinUpperLimit = UpperLimit.HasValue ? (age <= UpperLimit) : true;
return isWithinLowerLimit && isWithinUpperLimit;
}
}
元ネタにあるような特定の使い方だけしかしない内は、
こんなクラス化なんてやりすぎですが、
色々な使い方が出てきたときにはこういう方向に進んでいくことかと思います。
ついでに、Static フィールド の種類を増やす時に変更箇所が一箇所で済むよう
リフレクションとカスタム属性を導入してみます。
class Generation
{
class ItemAttribute : Attribute { }
[Item]public static readonly Generation Over60 = new Generation(60, null);
[Item]public static readonly Generation Over50 = new Generation(50, 59);
[Item]public static readonly Generation Over40 = new Generation(40, 49);
[Item]public static readonly Generation Over30 = new Generation(30, 39);
[Item]public static readonly Generation Over20 = new Generation(20, 29);
[Item]public static readonly Generation Under20 = new Generation(null, 19);
private static ReadOnlyCollection<Generation> _items = CreateItems();
private static ReadOnlyCollection<Generation> CreateItems()
{
var query =
from field in typeof(Generation).GetFields(BindingFlags.Static | BindingFlags.Public)
where (field.IsDefined(typeof(ItemAttribute), false))
select (field.GetValue(null) as Generation);
return new ReadOnlyCollection<Generation>(query.ToList());
}
public static Generation GetGeneration(int age)
{
return _items.Single(item => item.IsIncludeing(age));
}
private Generation(int? lowerLimit, int? upperLimit)
{
LowerLimit = lowerLimit;
UpperLimit = upperLimit;
}
public readonly int? LowerLimit;
public readonly int? UpperLimit;
public bool IsIncludeing(int age)
{
bool isWithinLowerLimit = LowerLimit.HasValue ? (LowerLimit <= age) : true;
bool isWithinUpperLimit = UpperLimit.HasValue ? (age <= UpperLimit) : true;
return isWithinLowerLimit && isWithinUpperLimit;
}
}
若干やりすぎた感があります。
そろそろ契約期間が切れるけど契約更新するつもりっす。
[参考]
ナオキにASP.NET(仮) : Code Rush Vs. ReSharper (海外の方の投稿)
CodeRush with Refactor! Pro
Lazy(T) クラス (System)
遅延初期化と聞いてすぐに連想されるのはレイジーロードパターンです。
以前、仮想プロキシによるレイジーロードについての記事を書きました。
今回、VirtualList<T> クラスの実装を、Lazy<T> クラスを使用するように変更してみます。
VirtualList<T> クラス
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// リストの仮想プロキシです。
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public sealed class VirtualList<T> : IList<T>
{
#region Constructors
/// <summary>
/// VirtualList<T> クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="loader">ローダー。</param>
/// <param name="isThreadSafeWhenLoading">
/// このインスタンスの遅延初期化処理をスレッドセーフにする場合は true。
/// このインスタンスの遅延初期化処理をスレッドセーフにしない場合は false。
/// </param>
/// <exception cref="System.ArgumentNullException">引数 loader が null です。</exception>
public VirtualList(IListLoader<T> loader, bool isThreadSafeWhenLoading = true)
: this(loader.Load, isThreadSafeWhenLoading)
{
}
/// <summary>
/// VirtualList<T> クラスの新しいインスタンスを初期化します。
/// </summary>
/// <param name="loader">ローダー。</param>
/// <param name="isThreadSafeWhenLoading">
/// このインスタンスの遅延初期化処理をスレッドセーフにする場合は true。
/// このインスタンスの遅延初期化処理をスレッドセーフにしない場合は false。
/// </param>
/// <exception cref="System.ArgumentNullException">引数 loader が null です。</exception>
public VirtualList(Func<IList<T>> loader, bool isThreadSafeWhenLoading = true)
{
if (loader == null)
{
throw new ArgumentNullException("loader");
}
this._lazyLoader = new Lazy<IList<T>>(loader, isThreadSafeWhenLoading);
}
#endregion
#region Fields
/// <summary>
/// レイジーローダーを取得します。
/// </summary>
private readonly Lazy<IList<T>> _lazyLoader;
#endregion
#region Properties
/// <summary>
/// ソースリストを取得します。
/// </summary>
private IList<T> Items
{
get
{
return this._lazyLoader.Value;
}
}
#endregion
#region IList<T> メンバ
/// <summary>
/// 指定したインデックスにある要素を取得または設定します。
/// </summary>
/// <param name="index">取得または設定する要素の、0 から始まるインデックス番号。</param>
/// <returns>指定したインデックスにある要素。</returns>
/// <exception cref="System.NotSupportedException">このプロパティが設定されていますが、VirtualList<T> が読み取り専用です。</exception>
/// <exception cref="System.ArgumentOutOfRangeException">index が VirtualList<T> の有効なインデックスではありません。</exception>
public T this[int index]
{
get
{
return this.Items[index];
}
set
{
this.Items[index] = value;
}
}
/// <summary>
/// VirtualList<T> 内での指定した項目のインデックスを調べます。
/// </summary>
/// <param name="item">IList<T> 内で検索するオブジェクト。</param>
/// <returns>リストに存在する場合は item のインデックス。それ以外の場合は -1。</returns>
public int IndexOf(T item)
{
return this.Items.IndexOf(item);
}
/// <summary>
/// VirtualList<T> の指定したインデックス位置に項目を挿入します。
/// </summary>
/// <param name="index">VirtualList<T> に挿入するオブジェクト。</param>
/// <param name="item">item を挿入する位置の、0 から始まるインデックス番号。</param>
/// <exception cref="System.NotSupportedException">VirtualList<T> は読み取り専用です。</exception>
/// <exception cref="System.ArgumentOutOfRangeException">index が VirtualList<T> の有効なインデックスではありません。</exception>
public void Insert(int index, T item)
{
this.Items.Insert(index, item);
}
/// <summary>
/// 指定したインデックス位置の VirtualList<T> 項目を削除します。
/// </summary>
/// <param name="index">削除する項目の 0 から始まるインデックス。</param>
/// <exception cref="System.NotSupportedException">VirtualList<T> は読み取り専用です。</exception>
/// <exception cref="System.ArgumentOutOfRangeException">index が VirtualList<T> の有効なインデックスではありません。</exception>
public void RemoveAt(int index)
{
this.Items.RemoveAt(index);
}
#endregion
#region ICollection<T> メンバ
/// <summary>
/// VirtualList<T> に格納されている要素の数を取得します。
/// </summary>
public int Count
{
get
{
return this.Items.Count;
}
}
/// <summary>
/// VirtualList<T> が読み取り専用かどうかを示す値を取得します。
/// </summary>
public bool IsReadOnly
{
get
{
return this.Items.IsReadOnly;
}
}
/// <summary>
/// VirtualList<T> に項目を追加します。
/// </summary>
/// <param name="item">VirtualList<T> に追加するオブジェクト。</param>
/// <exception cref="System.NotSupportedException">VirtualList<T> は読み取り専用です。</exception>
public void Add(T item)
{
this.Items.Add(item);
}
/// <summary>
/// VirtualList<T> からすべての項目を削除します。
/// </summary>
/// <exception cref="System.NotSupportedException">VirtualList<T> は読み取り専用です。</exception>
public void Clear()
{
this.Items.Clear();
}
/// <summary>
/// VirtualList<T> に特定の値が格納されているかどうかを判断します。
/// </summary>
/// <param name="item">VirtualList<T> 内で検索するオブジェクト。</param>
/// <returns>item が VirtualList<T> に存在する場合は true。それ以外の場合は false。</returns>
public bool Contains(T item)
{
return this.Items.Contains(item);
}
/// <summary>
/// VirtualList<T> の要素を System.Array にコピーします。System.Array の特定のインデックスからコピーが開始されます。
/// </summary>
/// <param name="array">VirtualList<T> から要素がコピーされる 1 次元の System.Array。System.Array には、0 から始まるインデックス番号が必要です。</param>
/// <param name="arrayIndex">コピーの開始位置となる、array の 0 から始まるインデックス番号。</param>
/// <exception cref="System.ArgumentException">
/// array が多次元です。
/// またはarrayIndex が array の長さ以上です。
/// またはコピー元の VirtualList<T> の要素数が、arrayIndex からコピー先の array の末尾までに格納できる数を超えています。
/// または型 T をコピー先の array の型に自動的にキャストすることはできません。
/// </exception>
/// <exception cref="System.ArgumentNullException">array が null です。</exception>
/// <exception cref="System.ArgumentOutOfRangeException">arrayIndex が 0 未満です。</exception>
public void CopyTo(T[] array, int arrayIndex)
{
this.Items.CopyTo(array, arrayIndex);
}
/// <summary>
/// VirtualList<T> 内で最初に見つかった特定のオブジェクトを削除します。
/// </summary>
/// <param name="item">VirtualList<T> から削除するオブジェクト。</param>
/// <returns>
/// item が VirtualList<T> から正常に削除された場合は true。それ以外の場合は false。
/// このメソッドは、item が元の VirtualList<T> に見つからない場合にも false を返します。
/// </returns>
/// <exception cref="System.NotSupportedException">VirtualList<T> は読み取り専用です。</exception>
public bool Remove(T item)
{
return this.Items.Remove(item);
}
#endregion
#region IEnumerable<T> メンバ
/// <summary>
/// コレクションを反復処理する列挙子を返します。
/// </summary>
/// <returns>コレクションを反復処理するために使用できる System.Collections.Generic.IEnumerator<T>。</returns>
public IEnumerator<T> GetEnumerator()
{
return this.Items.GetEnumerator();
}
#endregion
#region IEnumerable メンバ
/// <summary>
/// コレクションを反復処理する列挙子を返します。
/// </summary>
/// <returns>コレクションを反復処理するために使用できる System.Collections.IEnumerator オブジェクト。</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)this.Items).GetEnumerator();
}
#endregion
}
IListLoader<T> インターフェイス
using System;
using System.Collections.Generic;
/// <summary>
/// リストのローダーです。
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IListLoader<T>
{
/// <summary>
/// リストをロードします。
/// </summary>
/// <returns>リスト。</returns>
IList<T> Load();
}
VirtualList<T> クラスに Lazy<T> クラスを導入したことで、状態管理が不要になりました。
そして、遅延初期化処理をスレッドセーフにすることができるようになりました。
以前の実装のように、Lazy<T> クラスを使わずとも遅延初期化処理を実装することは容易です。
しかし、 Lazy<T> クラスを使用すれば、遅延初期化処理のスレッドセーフも容易に保証できるようになります。