Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在.NET中是否可以使用不可变数组?_C#_.net_Arrays_Immutability - Fatal编程技术网

C# 在.NET中是否可以使用不可变数组?

C# 在.NET中是否可以使用不可变数组?,c#,.net,arrays,immutability,C#,.net,Arrays,Immutability,是否可能以某种方式将System.Array标记为不可变。当放置在公共get/private集合之后时,它们不能被添加到其中,因为它需要重新分配和重新分配,但是消费者仍然可以设置他们想要的任何下标: public class Immy { public string[] { get; private set; } } 我认为readonly关键字可能会起作用,但没有这样的运气。我认为最佳做法是在公共API中使用IList,而不是数组readonly将阻止在构造函数之外设置成员变量,但正

是否可能以某种方式将
System.Array
标记为不可变。当放置在公共get/private集合之后时,它们不能被添加到其中,因为它需要重新分配和重新分配,但是消费者仍然可以设置他们想要的任何下标:

public class Immy
{
    public string[] { get; private set; }
}

我认为
readonly
关键字可能会起作用,但没有这样的运气。

我认为最佳做法是在公共API中使用IList,而不是数组readonly将阻止在构造函数之外设置成员变量,但正如您所发现的,不会阻止人们在数组中分配元素

// bad code
// could still do Path.InvalidPathChars[0] = 'A';
public sealed class Path {
   public static readonly char[] InvalidPathChars = 
      { '\"', '<', '>', '|' };
}
有关更多信息,请参阅


编辑:数组不能是只读的,但是可以通过Array.AsReadOnly()将它们转换为只读的IList实现,正如@shahkalpesh所指出的。

可能就是您想要的。它没有添加()方法。

可以使用Array.AsReadOnly方法返回。

建议返回数组的副本。这样,消费者就无法更改阵列中的项目

// bad code
// could still do Path.InvalidPathChars[0] = 'A';
public sealed class Path {
   public static readonly char[] InvalidPathChars = 
      { '\"', '<', '>', '|' };
}
//错误代码
//仍然无法执行Path.InvalidPathChars[0]=“A”;
公共密封类路径{
公共静态只读字符[]InvalidPathChars=
{ '\"', '', '|' };
}
这些更好:

public static ReadOnlyCollection<char> GetInvalidPathChars(){
   return Array.AsReadOnly(InvalidPathChars);
}

public static char[] GetInvalidPathChars(){
   return (char[])InvalidPathChars.Clone();
}
公共静态只读集合GetInvalidPathChars(){
返回数组.AsReadOnly(InvalidPathChars);
}
公共静态字符[]GetInvalidPathChars(){
返回(char[])InvalidPathChars.Clone();
}

这些例子都是直接从书中获得的。

唯一需要添加的是数组意味着可变性。当您从函数返回数组时,您是在向客户端程序员建议,他们可以/应该更改内容。

继Matt的回答之后,IList是数组的一个完整抽象接口,因此它允许添加、删除等操作。我不是确定为什么Lippert似乎建议在需要不变性的情况下将其作为IEnumerable的替代品。(编辑:,因为如果您喜欢,IList实现可以为这些变异方法抛出异常)

也许还有一件事需要记住,列表中的项目也可能具有可变状态。如果您确实不希望调用方修改这种状态,您可以选择:

确保列表中的项目是不可变的(如您的示例:string是不可变的)

返回所有内容的深度克隆,因此在这种情况下,无论如何都可以使用数组

返回一个接口,该接口允许对项目进行只读访问:

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
{
公共字符串ValuableCustomerData{get;}
}
类可变,类可变
{
公共字符串ValuableCustomerData{get;set;}
}
公共类移民
{
私有列表_mutableList=新列表();
公共IEnumerable不可变项
{
获取{return _mutableList.Cast();}
}
}

请注意,从IImmutable接口访问的每个值本身都必须是不可变的(例如字符串),或者是您动态创建的副本。

您希望做的最好的事情是扩展现有集合以构建您自己的集合。最大的问题是,它的工作方式必须不同于所有现有集合类型,因为每次调用都必须返回一个新集合。

请参阅基类库中的(目前正在预览中)。

.NET倾向于在所有情况下避开阵列,但最简单和最传统的用例除外。对于其他所有情况,都有各种可枚举/集合实现

如果要将一组数据标记为不可变,则超出了传统数组提供的功能。.NET提供了等效的功能,但从技术上讲不是以数组的形式。要从数组中获取不可变的集合,请使用:

immutable
将是一个实例。作为更通用的用例,您可以从任何通用实现创建一个

var immutable = new ReadOnlyCollection<char>(new List<char>(mutable));

更准确地说,它没有公开的Add()方法。它确实有一个,因为接口需要它。但是好的接口代码会检查。在调用它之前先只读,因为这样做会导致异常。这与不可变数组不同--
ReadOnlyCollection
不允许任何修改,但它只包装一个常规的
列表,它创建者仍然可以更改。@HenryJackson最终,.NET中的所有数据都可以通过反射进行更改。因此,有意义的不可变性只不过是使不可访问的不可变性变得不可访问。如果对可变集合的引用被丢弃,则对可变集合的只读包装器当然会被视为不可变的,从而造成变异ible.@AndrewArnott:不幸的是,无论是
ReadOnlyCollection
还是任何其他类似的框架类型,都没有提供任何方法来保证实例的备份存储是不可变的(例如,因为包装器创建了原始数组的克隆,它在宇宙中的任何地方都是对原始数组的唯一引用)。在许多情况下,接收集合的代码应该创建快照,除非它是不可变的,在这种情况下,不应该麻烦,但无法确定是否需要快照。如果我没有错的话,不可变和只读是不一样的。这是一种约定还是客户端需要快照的更具体原因这种行为?该属性缺少名称,不是吗?该属性称为“不可变”-从这个意义上说,字符串是不可变的(除非您在不安全的代码中访问字符指针,因为它们是被占用的,所以您确实不应该这样做。)请记住,不可变数组只与其成员一样不可变。@PhilWhittington他在代码中的意思是,属性缺少名称。这在语法上是不正确的。但它并不是真正的工作示例。我知道这是几年前的事,但Svish指出,您应该有公共字符串[]variablename{…}他说,那财产不见了
// 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' }