声明一个const数组

是否可以写出类似于下面的内容?

public const string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
546957 次浏览

我相信你只能让它只读。

数组可能是那些只能在 运行时。常量必须在编译时求值。尝试使用"readonly" 而不是"const".

是的,但是你需要声明它是readonly而不是const:

public static readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };

原因是const只能应用于编译时已知值的字段。您所展示的数组初始化器不是c#中的常量表达式,因此会产生编译器错误。

声明它为readonly可以解决这个问题,因为该值直到运行时才初始化(尽管可以保证在第一次使用数组之前已经初始化)。

根据你最终想要实现的目标,你也可以考虑声明一个枚举:

public enum Titles { German, Spanish, Corrects, Wrongs };
你不能创建一个“const”数组,因为数组是对象,只能是对象 在运行时创建,const实体在编译时解析 你可以做的是声明你的数组为"readonly"。这是 与const的效果相同,只是该值可以在运行时设置。它只能是 设置一次,之后它就是一个只读(即const)值

您可以将数组声明为readonly,但请记住,您可以更改readonly数组的元素。

public readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
...
Titles[0] = "bla";

可以考虑像Cody建议的那样使用enum或IList。

public readonly IList<string> ITitles = new List<string> {"German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();

对于我的需要,我定义了static数组,而不是不可能的const,它工作: # EYZ0 < / p >

你可以采用一种不同的方法:定义一个常量字符串来表示你的数组,然后在你需要它的时候把字符串分割成一个数组。

const string DefaultDistances = "5,10,15,20,25,30,40,50";
public static readonly string[] distances = DefaultDistances.Split(',');

这种方法为您提供了一个可以存储在配置中并在需要时转换为数组的常量。

这是一种做你想做的事情的方法:

using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;


public ReadOnlyCollection<string> Titles { get { return new List<string> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();}}

它非常类似于做一个只读数组。

作为一种替代方法,要解决使用只读数组可以修改元素的问题,可以使用静态属性。(单个元素仍然可以更改,但这些更改只会在数组的本地副本上进行。)

public static string[] Titles
{
get
{
return new string[] { "German", "Spanish", "Corrects", "Wrongs"};
}
}

当然,这不是特别有效,因为每次都会创建一个新的字符串数组。

从c# 6开始,你可以这样写:

public static string[] Titles => new string[] { "German", "Spanish", "Corrects", "Wrongs" };

参见:c#:新的和改进的c# 6.0(特别是“表达式体函数和属性”章节)

这将使一个只读的静态属性,但它仍然允许您更改返回的数组的内容,但当您再次调用该属性时,您将再次获得原始的、未更改的数组。

为了澄清,这段代码与(或实际上是缩写)相同:

public static string[] Titles
{
get { return new string[] { "German", "Spanish", "Corrects", "Wrongs" }; }
}
请注意,这种方法有一个缺点:每个引用实际上都会实例化一个新数组,所以如果你使用的是一个非常大的数组,这可能不是最有效的解决方案。 但是如果你重复使用同一个数组(例如,通过将它放在私有属性中),它将再次打开改变数组内容的可能性

如果你想要一个不可变数组(或列表),你也可以使用:

public static IReadOnlyList<string> Titles { get; } = new string[] { "German", "Spanish", "Corrects", "Wrongs" };

但是,这仍然有更改的风险,因为你仍然可以将其转换回字符串[]并更改内容,如下所示:

((string[]) Titles)[1] = "French";

.NET Framework v4.5+解决方案改进于tdbeckett的回答:

using System.Collections.ObjectModel;


// ...


public ReadOnlyCollection<string> Titles { get; } = new ReadOnlyCollection<string>(
new string[] { "German", "Spanish", "Corrects", "Wrongs" }
);

注意:假定集合在概念上是常量,在级别将其声明为static可能是有意义的。

上面的:

  • 初始化属性的隐式支持字段一次数组。

    • 注意,{ get; } -即只声明属性getter -是使属性本身隐式只读的原因(试图将readonly{ get; }结合起来实际上是一个语法错误)。

    • 或者,您可以省略{ get; }并添加readonly来创建字段而不是属性,就像问题中那样,但是将public数据成员作为属性而不是字段是一个好习惯。

    • 李< / ul > < / >
    • 创建一个数组——< em > < / em >结构(允许索引访问),它是真实且健壮的只读(概念上不变,一旦创建),两者都是关于:

      • 阻止修改集合作为一个整体(例如通过删除或添加元素,或通过将一个新集合分配给变量)。
      • <李> # EYZ0。< br > (甚至间接修改是不可能的-不像IReadOnlyList<T>解决方案,其中(string[])强制转换可用于获得对元素的写访问权,如Mjepsen的回答很有帮助所示。
        同样的漏洞也适用于IReadOnlyCollection<T> 接口,尽管在名称上与 ReadOnlyCollection甚至不支持索引访问相似,但这使得它根本不适合提供类似数组的访问 李< / ul > < / >

如果你在IReadOnlyList接口后面声明了一个数组,你会得到一个常量数组,在运行时声明常量值:

public readonly IReadOnlyList<string> Titles = new [] {"German", "Spanish", "Corrects", "Wrongs" };

在. net 4.5及更高版本中可用。

为了完整起见,现在我们还可以使用# EYZ0s。这应该是真正不可变的:

public readonly static ImmutableArray<string> Titles =
ImmutableArray.Create(new[] { "German", "Spanish", "Corrects", "Wrongs" });

需要System.Collections.Immutable NuGet引用。

这是唯一正确的答案。目前还不能这样做。

所有其他答案都建议使用静态只读变量,其中类似的,但不一样的为常量。常量被硬编码到程序集中。静态只读变量只能设置一次,可能是在初始化对象时。

这些有时是可以互换的,但并非总是如此。

编辑:我想我应该把这个扔进来,因为似乎问这个问题的人对数组有点模糊。声明数组时,它是指向包含该数组的内存段的指针。它非常简单,因为它只是一个地址,没有复杂的逻辑控制它是否可读或可写。它给你一个指针,你可以用它做任何你想做的事情。

这就是为什么创建一个不可变数组有点棘手的部分原因。你可以写一个包装数组的类并且只允许通过返回一个副本来读取它,但这样它就不再只是一个数组了,它是一个包装数组的对象。

有些人建议使用static或readonly来模拟创建const数组时看到的行为。这些药物有一些副作用,对普通读者来说可能不太明显。

要真正获得const数组,需要对c#和MSIL底层代码进行更新,以允许从数组中读取,但不允许写入。本页上的其他建议是解决这个问题的各种创造性方法。

最好的选择:

public static readonly byte[] ZeroHash = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

快速的解决方法,说不定对别人有帮助。我需要string[]作为Attribute的参数(在某些理论中为测试用例传递内联数据)。readonly在这里没用。但是,我最后做的是:

const string foo = "a|b|c";


[InlineData(foo)]
public void Test(string fooString)
{
var foo = fooString.Split("|"); // foo == {"a","b","c"}
...
}