はじめに
.NET Coreのソースを読む!第一回 List パート1の続きです。
ソースリンク
ソースを読む!
プロパティ
public int Capacity
{
get
{
return _items.Length;
}
set
{
if (value < _size)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
}
if (value != _items.Length)
{
if (value > 0)
{
T[] newItems = new T[value];
if (_size > 0)
{
Array.Copy(_items, 0, newItems, 0, _size);
}
_items = newItems;
}
else
{
_items = s_emptyArray;
}
}
}
}
Capacityは、List内部で保持している配列の長さになりますね。Listの長さではなく、内部でデータを実際に格納している配列の長さです。Addしていって、Capacityを超えそうになったら、自動で拡張するようになっていますね。前回も記載しましたが、最初からデータ量が多いと分かっている場合は、Capacityをコンストラクタで指定するのが定石ですね。
「_size」が実際のデータの長さなので、それを超える場合は、エラーにするのですね。そもそも、Listのインスタンスを作成した後に、Capacityが変えれるのは知りませんでした。Capacityを変えると、内部で新たな配列を作成するので、ちゃんと理解しておかないといけませんね。
valueに0が指定されたら「s_emptyArray」をちゃんと初期化時に使っていますね。共通ライブラリなので、少しでもメモリを節約する努力だと思うのですが、どこの誰が、Capacityに0を指定するのでしょうね。。。
// Read-only property describing how many elements are in the List.
public int Count => _size;
これで「_size」の値を読み取り専用で公開できるのですね。
6.0からの機能で、「expression-bodied function member」というのですね。
私の現場では、5.0なので、これは使ったことありません。
しかし、公開はCountで、内部は_sizeってのはなんででしょうか?
最初に_sizeにしちゃって、変えようと思ったら、シリアライズ対策のために変えれなかったのかな。
あと、IListではCountプロパティで、LINQにもCountメソッド(拡張メソッドだけど)があるから紛らわしいですね。
bool IList.IsFixedSize => false;
bool ICollection<T>.IsReadOnly => false;
bool IList.IsReadOnly => false;
bool ICollection.IsSynchronized => false;
Listは、固定長ではなく、読み取り専用でもなく、スレッドセーフでもないということですね。
おお、bool IList.IsFixedSize => false;
みたいに、特定のインタフェースで参照したときしか参照できない方法でオーバーライドしてますね。IList.IsFixedSize
とすると、IListインタフェースで参照したときしか、IsFixedSizeプロパティが参照できなくなりますね。
しかし、このC#の機能は私は一度も使ったことがないのですが、標準ライブラリではガンガン使っているのですね。
object ICollection.SyncRoot
{
get
{
if (_syncRoot == null)
{
Interlocked.CompareExchange<object>(ref _syncRoot, new object(), null);
}
return _syncRoot;
}
}
SyncRootプロパティは、誰かが同期処理をするときに使うのかな?使ったことないけど。
Interlocked.CompareExchange
は、便利そうですね。詳細は、以下の「(4) 値を比較し、等しければ指定した別の値を代入する」に説明がありました。
ロックして、値を初期化するときに便利そうですが、逆に分かりにくいかもしれませんね。
public T this[int index]
{
get
{
// Following trick can reduce the range check by one
if ((uint)index >= (uint)_size)
{
ThrowHelper.ThrowArgumentOutOfRange_IndexException();
}
return _items[index];
}
set
{
if ((uint)index >= (uint)_size)
{
ThrowHelper.ThrowArgumentOutOfRange_IndexException();
}
_items[index] = value;
_version++;
}
}
インデクサーですね。
範囲外の場合はエラーにしますね。
インデクサーでは自動で拡張してくれませんよ!!お気をつけて。
自動で拡張するのはAddメソッドでっせ。
object IList.this[int index]
{
get
{
return this[index];
}
set
{
ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value);
try
{
this[index] = (T)value;
}
catch (InvalidCastException)
{
ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T));
}
}
}
おお、Genericではない型で参照されたときのインデクサーの実装は、分けているのですね。
次回はスタティックメソッドかな。
ライセンス
色々ソースを載せているので、念のためライセンスへリンクしておきます。