System.Array
どういうわけか aを不変としてマークすることは可能ですか? public-get/private-set の後ろに置くと、再割り当てと再割り当てが必要になるため、それらを追加することはできませんが、消費者は必要な添え字を設定できます。
public class Immy
{
public string[] { get; private set; }
}
私はreadonly
キーワードがトリックを行うかもしれないと思ったが、そのような運はなかった.
System.Array
どういうわけか aを不変としてマークすることは可能ですか? public-get/private-set の後ろに置くと、再割り当てと再割り当てが必要になるため、それらを追加することはできませんが、消費者は必要な添え字を設定できます。
public class Immy
{
public string[] { get; private set; }
}
私はreadonly
キーワードがトリックを行うかもしれないと思ったが、そのような運はなかった.
フレームワーク設計ガイドラインでは、配列のコピーを返すことを提案しています。そうすれば、コンシューマーは配列から項目を変更できません。
// bad code
// could still do Path.InvalidPathChars[0] = 'A';
public sealed class Path {
public static readonly char[] InvalidPathChars =
{ '\"', '<', '>', '|' };
}
これらはより良いです:
public static ReadOnlyCollection<char> GetInvalidPathChars(){
return Array.AsReadOnly(InvalidPathChars);
}
public static char[] GetInvalidPathChars(){
return (char[])InvalidPathChars.Clone();
}
例は本からそのままです。
ReadOnlyCollection<T>
おそらくあなたが探しているものです。メソッドはありませんAdd()
。
基本クラス ライブラリで使用できるようになった不変コレクションを参照してください(現在プレビュー中)。
Array.AsReadOnly メソッドを使用して返すことができます。
この正確な理由から、パブリック API では配列ではなく IList<T> を使用することをお勧めします。readonlyは、メンバー変数がコンストラクターの外部で設定されるのを防ぎますが、発見したように、人々が配列内の要素を割り当てるのを防ぎません。
詳細については、やや有害と見なされる配列を参照してください。
編集:配列は読み取り専用にすることはできませんが、@shahkalpesh が指摘しているように、Array.AsReadOnly() を介して読み取り専用の IList 実装に変換できます。
.NET は、最も単純で最も伝統的なユース ケースを除いて、配列から遠ざかる傾向があります。他のすべてについては、さまざまな列挙可能/コレクションの実装があります。
データのセットを不変としてマークしたい場合、従来の配列によって提供される機能を超えています。.NET は同等の機能を提供しますが、技術的には配列の形式ではありません。配列から不変のコレクションを取得するには、次を使用しますArray.AsReadOnly<T>
。
var mutable = new[]
{
'a', 'A',
'b', 'B',
'c', 'C',
};
var immutable = Array.AsReadOnly(mutable);
immutable
インスタンスになりReadOnlyCollection<char>
ます。より一般的な使用例として、ReadOnlyCollection<T>
任意の汎用IList<T>
実装から を作成できます。
var immutable = new ReadOnlyCollection<char>(new List<char>(mutable));
一般的な実装でなければならないことに注意してください。従来IList
の配列ではこのメソッドを使用できません。これは のみを実装しIList
ます。Array.AsReadOnly<T>
これにより、通常は従来の配列ではアクセスできない汎用実装へのアクセスを取得するための迅速な手段として使用できる可能性が明らかになります。
ReadOnlyCollection<T>
不変配列に期待されるすべての機能にアクセスできます。
// Note that .NET favors Count over Length; all but traditional arrays use Count:
for (var i = 0; i < immutable.Count; i++)
{
// this[] { get } is present, as ReadOnlyCollection<T> implements IList<T>:
var element = immutable[i]; // Works
// this[] { set } has to be present, as it is required by IList<T>, but it
// will throw a NotSupportedException:
immutable[i] = element; // Exception!
}
// ReadOnlyCollection<T> implements IEnumerable<T>, of course:
foreach (var character in immutable)
{
}
// LINQ works fine; idem
var lowercase =
from c in immutable
where c >= 'a' && c <= 'z'
select c;
// You can always evaluate IEnumerable<T> implementations to arrays with LINQ:
var mutableCopy = immutable.ToArray();
// mutableCopy is: new[] { 'a', 'A', 'b', 'B', 'c', 'C' }
var lowercaseArray = lowercase.ToArray();
// lowercaseArray is: new[] { 'a', 'b', 'c' }
追加する唯一のことは、配列は可変性を意味するということです。関数から配列を返すときは、クライアントプログラマーに、変更できる/変更する必要があることを提案しています。
Matt の回答に加えて、IList は配列への完全な抽象インターフェイスであるため、追加、削除などを行うことができます。不変性が必要な場合に、Lippert が IEnumerable の代替としてそれを提案しているように見える理由がわかりません。(編集: IList実装は、そのようなものが好きな場合は、これらの変更メソッドに対して例外をスローできるためです)。
リスト上のアイテムも変更可能な状態になる可能性があることに注意してください。呼び出し元にそのような状態を変更させたくない場合は、いくつかのオプションがあります。
リストの項目が不変であることを確認してください(例のように、文字列は不変です)。
すべての深いクローンを返すので、その場合はとにかく配列を使用できます。
項目への読み取り専用アクセスを与えるインターフェイスを返します。
interface IImmutable
{
public string ValuableCustomerData { get; }
}
class Mutable, IImmutable
{
public string ValuableCustomerData { get; set; }
}
public class Immy
{
private List<Mutable> _mutableList = new List<Mutable>();
public IEnumerable<IImmutable> ImmutableItems
{
get { return _mutableList.Cast<IMutable>(); }
}
}
IImmutable インターフェイスからアクセスできるすべての値は、それ自体が不変 (文字列など) であるか、その場で作成するコピーでなければならないことに注意してください。
最善の方法は、既存のコレクションを拡張して独自のコレクションを作成することです。大きな問題は、すべての呼び出しが新しいコレクションを返す必要があるため、既存のすべてのコレクション タイプとは異なる動作をしなければならないことです。