.NET中是否可以使用不可变数组?
是否有可能以某种方式将System.Array
标记为不可变。 当置于public-get / private-set之后,它们无法添加,因为它需要重新分配和重新分配,但消费者仍然可以设置他们希望的任何下标:
public class Immy { public string[] { get; private set; } }
我认为readonly
关键字可能会起作用,但没有这样的运气。
ReadOnlyCollection
可能就是你要找的东西。 它没有Add()
方法。
框架设计指南建议返回Array的副本。 这样,消费者就无法从arrays中更改项目。
// bad code // could still do Path.InvalidPathChars[0] = 'A'; public sealed class Path { public static readonly char[] InvalidPathChars = { '\"', '<', '>', '|' }; }
这些更好:
public static ReadOnlyCollection GetInvalidPathChars(){ return Array.AsReadOnly(InvalidPathChars); } public static char[] GetInvalidPathChars(){ return (char[])InvalidPathChars.Clone(); }
这些例子直接来自本书。
请参阅基类库中现在可用的不可变集合 (当前处于预览中)。
您可以使用Array.AsReadOnly方法返回。
我认为最佳做法是在公共API中使用IList
有关更多信息,请参阅有些有害的arrays 。
编辑:数组不能只读,但可以通过@shahkalpesh指出的Array.AsReadOnly()转换为只读的IList实现。
除了最简单和最传统的用例之外,.NET倾向于远离数组。 对于其他一切,有各种可枚举/集合实现。
当您想将一组数据标记为不可变时,您将超越传统数组提供的function。 .NET提供了相同的function,但技术上并不是数组的forms。 要从数组中获取不可变集合,请使用Array.AsReadOnly
:
var mutable = new[] { 'a', 'A', 'b', 'B', 'c', 'C', }; var immutable = Array.AsReadOnly(mutable);
immutable
将是一个ReadOnlyCollection
实例。 作为更通用的用例,您可以从任何通用IList
实现创建ReadOnlyCollection
。
var immutable = new ReadOnlyCollection(new List (mutable));
请注意,它必须是通用实现; 普通的旧IList
不起作用,这意味着你不能在传统的数组上使用这个方法,而传统的数组只实现了IList
。 这揭示了使用Array.AsReadOnly
作为获取通常通过传统arrays无法访问的通用实现的快速方法的可能性。
ReadOnlyCollection
将允许您访问您期望从不可变数组中获得的所有function:
// 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 implements IList : var element = immutable[i]; // Works // this[] { set } has to be present, as it is required by IList , but it // will throw a NotSupportedException: immutable[i] = element; // Exception! } // ReadOnlyCollection implements IEnumerable , 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 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' }
唯一要补充的是Arrays 意味着可变性。 当您从函数返回一个数组时,您建议客户端程序员可以/应该更改内容。
继Matt的回答之后,IList是一个完整的数组抽象接口,所以它允许添加,删除等。我不确定为什么Lippert似乎建议它作为IEnumerable的替代品,需要不变性。 ( 编辑:因为IList实现可以为那些变异方法抛出exception,如果你喜欢那种东西)。
也许另外要记住的是,列表中的项目也可能具有可变状态。 如果你真的不希望调用者修改这种状态,你有一些选择:
确保列表中的项是不可变的(如示例所示:string是不可变的)。
返回所有内容的深层克隆,所以在这种情况下你无论如何都可以使用数组。
返回一个可以只读访问项目的接口:
interface IImmutable { public string ValuableCustomerData { get; } } class Mutable, IImmutable { public string ValuableCustomerData { get; set; } } public class Immy { private List _mutableList = new List (); public IEnumerable ImmutableItems { get { return _mutableList.Cast(); } } }
请注意,可从IImmutable接口访问的每个值本身必须是不可变的(例如字符串),或者是您即时创建的副本。
您可以希望做的最好的事情是扩展现有的集合以构建您自己的集合。 最大的问题是它必须以不同于每个现有集合类型的方式工作,因为每个调用都必须返回一个新集合。
您可能想查看我对类似问题的回答,以获得有关在对象上公开集合的更多想法。