C# .NET 中是否可以使用不可变数组?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/210428/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Are immutable arrays possible in .NET?
提问by Neil C. Obremski
Is it possible to somehow mark a System.Array
as immutable. When put behind a public-get/private-set they can't be added to, since it requires re-allocation and re-assignment, but a consumer can still set any subscript they wish:
是否有可能以某种方式将 a 标记System.Array
为不可变的。当放在 public-get/private-set 后面时,它们不能被添加到,因为它需要重新分配和重新分配,但消费者仍然可以设置他们想要的任何下标:
public class Immy
{
public string[] { get; private set; }
}
I thought the readonly
keyword might do the trick, but no such luck.
我认为readonly
关键字可能会起作用,但没有这样的运气。
采纳答案by Ray Jezek
ReadOnlyCollection<T>
is probably what you are looking for. It doesn't have an Add()
method.
ReadOnlyCollection<T>
可能是您正在寻找的。它没有Add()
方法。
回答by Matt
I believe best practice is to use IList<T> rather than arrays in public APIs for this exact reason. readonlywill prevent a member variable from being set outside of the constructor, but as you discovered, won't prevent people from assigning elements in the array.
我相信最佳做法是在公共 API 中使用 IList<T> 而不是数组,正是出于这个原因。readonly将阻止在构造函数之外设置成员变量,但正如您发现的那样,不会阻止人们分配数组中的元素。
See Arrays Considered Somewhat Harmfulfor more information.
有关更多信息,请参阅被认为有些有害的数组。
Edit:Arrays can't be read only, but they can be converted to read-only IList implementations via Array.AsReadOnly() as @shahkalpesh points out.
编辑:数组不能是只读的,但正如@shahkalpesh 指出的那样,它们可以通过 Array.AsReadOnly() 转换为只读的 IList 实现。
回答by shahkalpesh
You could use Array.AsReadOnly method to return.
您可以使用 Array.AsReadOnly 方法返回。
回答by Quantenmechaniker
The Framework Design Guidelinessuggest returning a copy of the Array. That way, consumers can't change items from the array.
该框架设计指南建议返回数组的副本。这样,消费者就无法更改数组中的项目。
// bad code
// could still do Path.InvalidPathChars[0] = 'A';
public sealed class Path {
public static readonly char[] InvalidPathChars =
{ '\"', '<', '>', '|' };
}
these are better:
这些更好:
public static ReadOnlyCollection<char> GetInvalidPathChars(){
return Array.AsReadOnly(InvalidPathChars);
}
public static char[] GetInvalidPathChars(){
return (char[])InvalidPathChars.Clone();
}
The examples are straight from the book.
这些例子直接来自书中。
回答by tsimon
The only thing to add is that Arrays implymutability. When you return an Array from a function, you are suggesting to the client programmer that they can/should change things.
唯一要补充的是,数组意味着可变性。当您从函数返回一个数组时,您是在向客户端程序员建议他们可以/应该更改某些内容。
回答by Daniel Earwicker
Further to Matt's answer, IList is a complete abstract interface to an array, so it allows add, remove, etc. I'm not sure why Lippert appears to suggest it as an alternative to IEnumerable where immutability is needed. (Edit:because the IList implementation can throw exceptions for those mutating methods, if you like that kind of thing).
除了 Matt 的回答之外,IList 是一个完整的数组抽象接口,因此它允许添加、删除等。我不知道为什么 Lippert 似乎建议将它作为 IEnumerable 的替代品,其中需要不变性。(编辑:因为 IList 实现可以为那些变异方法抛出异常,如果你喜欢那种事情的话)。
Maybe another thing to bear in mind that the items on the list may also have mutable state. If you really don't want the caller to modify such state, you have some options:
也许要记住的另一件事是列表中的项目也可能具有可变状态。如果你真的不希望调用者修改这样的状态,你有一些选择:
Make sure the items on the list are immutable (as in your example: string is immutable).
确保列表中的项目是不可变的(如您的示例:字符串是不可变的)。
Return a deep clone of everything, so in that case you could use an array anyway.
返回所有内容的深度克隆,因此在这种情况下,您无论如何都可以使用数组。
Return an interface that gives readonly access to an item:
返回一个接口,该接口提供对项目的只读访问权限:
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>(); }
}
}
Note that every value accessible from the IImmutable interface must itself be immutable (e.g. string), or else be a copy that you make on-the-fly.
请注意,可从 IImmutable 接口访问的每个值本身必须是不可变的(例如字符串),或者是您即时制作的副本。
回答by Rick Minerich
The best you can hope to do is extend an existing collection to build your own. The big issue is that it would have to work differently than every existing collection type because every call would have to return a new collection.
您希望做的最好的事情就是扩展现有的收藏以构建您自己的收藏。最大的问题是它必须与每个现有集合类型的工作方式不同,因为每次调用都必须返回一个新集合。
回答by JarrettV
Please see Immutable Collections Now Availablein the base class library (currently in preview).
请参阅基类库中的不可变集合现在可用(当前处于预览状态)。
回答by Zenexer
.NET tends to steer away from arrays for all but the simplest and most traditional use cases. For everything else, there are various enumerable/collection implementations.
除了最简单和最传统的用例外,.NET 倾向于避免使用数组。对于其他一切,有各种可枚举/集合实现。
When you want to mark a set of data as immutable, you're going beyond the capability provided by a traditional array. .NET provides equivalent capability, but not technically in the form of an array. To get an immutable collection from an array, use ">Array.AsReadOnly<T>
:
当您想将一组数据标记为不可变时,您就超出了传统数组提供的功能。.NET 提供了等效的功能,但技术上不是以数组的形式。要从数组中获取不可变集合,请使用">Array.AsReadOnly<T>
:
var mutable = new[]
{
'a', 'A',
'b', 'B',
'c', 'C',
};
var immutable = Array.AsReadOnly(mutable);
immutable
will be a ">ReadOnlyCollection<char>
instance. As a more general use case, you can create a ">ReadOnlyCollection<T>
from any generic ">IList<T>
implementation.
immutable
将是一个">ReadOnlyCollection<char>
实例。作为更一般的用例,您可以">ReadOnlyCollection<T>
从任何通用">IList<T>
实现创建一个。
var immutable = new ReadOnlyCollection<char>(new List<char>(mutable));
Note that it has to be a generic implementation; plain old IList
won't work, meaning that you can't use this method on a traditional array, which only implements IList
. This brings to light the possibility of using ">Array.AsReadOnly<T>
as a quick means of obtaining access to generic implementations that are normally inaccessible via a traditional array.
请注意,它必须是通用实现;简单的旧方法IList
不起作用,这意味着您不能在传统数组上使用此方法,该数组仅实现IList
. 这揭示了使用">Array.AsReadOnly<T>
作为获取对通常无法通过传统数组访问的通用实现的访问的快速方法的可能性。
">ReadOnlyCollection<T>
will give you access to all of the features that you would expect from an immutable array:
">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' }