声明一个const数组


457

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

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

可以使用static,public static string [] Titles = new string [] {“ German”,“ Spanish”};

Answers:


690

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

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

原因是 const只能应用于其值在编译时已知的字段。您显示的数组初始值设定项在C#中不是常量表达式,因此会产生编译器错误。

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

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

public enum Titles { German, Spanish, Corrects, Wrongs };

115
注意,这里的数组当然不是只读的。标题[2] =“威尔士”; 会在运行时很好地工作
Marc Gravell

46
您可能也希望它是静态的
tymtam

4
如何在方法主体而不是在类主体中声明“ const”数组?
serhio 2014年

19
不好意思,但是const也意味着静态。将数组声明为只读状态尚无法解决。它必须readonly static具有所请求语义的任何相似之处。
安东

3
@Anton,您和您的“追随者”是否已被删除?对我static来说,不需要使其工作,它只是增加了引用的可能性而Titles没有实例,但是消除了为不同实例更改值的可能性(例如,可以使构造函数具有参数,具体取决于您更改该readonly字段的值)。
Sinatr

57

您可以将array声明为readonly,但是请记住,可以更改readonlyarray的元素。

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();

31
在.NET 4.5及更高版本中,您可以将列表声明为IReadOnlyList <string>而不是IList <string>。
2014年

为了清楚起见,您仍然可以更改IReadOnlyList中的值(只是不能添加或删除元素)。但是,是的,将其声明为IReadOnlyList会比IList更好。
KevinVictor

问题是关于const不是readonly……
Yousha Aleayoub

50

您无法创建'const'数组,因为数组是对象,只能在运行时创建,而const实体则在编译时解析。

您可以做的是将数组声明为“只读”。除了可以在运行时设置值之外,这与const具有相同的作用。它只能设置一次,此后是只读(即const)值。


2
这篇文章重点说明了为什么不能将数组声明为常量
Radderz '19

1
可以声明一个常量数组;问题是用一个恒定值初始化它。我想到的唯一一个工作示例const int[] a = null;不是很有用,但实际上是数组常量的实例。
waldrumpus

这是唯一正确的答案。
Yousha Aleayoub

17

从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 []并更改其内容,例如:

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

1
在这种情况下,使用财产代替田地有什么好处?
nicolay.anykienko

2
字段无法在每次调用时返回新对象。属性本质上是一种“变相功能”。
mjepson '16

1
如果您指的是最后一个选项,则可以使用字段或属性来完成,但是由于它是公共属性,因此我更喜欢使用Property。自引入“属性”以来,我从未使用过公共领域。
mjepson '16

1
第一种方法还有另一个缺点:Titles[0]例如,如果您分配给,则不会出现编译错误-实际上,分配尝试被悄悄地忽略了。再加上每次重新创建阵列的效率低下,我想知道这种方法是否值得展示。相比之下,第二种方法是有效的,您必须竭尽全力来克服不变性。
mklement0

6

如果在IReadOnlyList接口后面声明一个数组,则将获得一个在运行时声明的具有常量值的常量数组:

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

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


6

一个.NET框架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以创建字段而不是属性,就像在问题中一样,但是将公共数据成员公开为属性而不是字段是一种好习惯。

  • 创建一个类似于数组的结构(允许索引访问),该结构真正且健壮地为只读(概念上是常量,一旦创建),就以下方面而言:

    • 阻止整个集合的修改(例如,通过删除或添加元素,或通过将新集合分配给变量)。
    • 防止修改单个元素
      (即使是间接的修改是不可能的-不像一个IReadOnlyList<T>解决方案,其中一个(string[])投可以用来获得对要素的写入权限,如图mjepsen的帮助的解答
      同样的漏洞适用于IReadOnlyCollection<T> 接口,其中,尽管在名称相似进行分类 ReadOnlyCollection甚至不支持索引访问,这使其从根本上不适合提供类似数组的访问。)

1
@mortb:不幸的是,IReadOnlyCollection它不支持索引访问,因此不能在这里使用。此外,像IReadOnlyList(确实具有索引访问权)一样,它也很容易通过回退到来进行元素操作string[]。换句话说:(ReadOnlyCollection您不能将其转换为字符串数组)是最可靠的解决方案。不使用吸气剂是一种选择(我已经更新了答案以指出这一点),但是对于公共数据,最好保留一个属性。
mklement0

5

对于我的需要,我定义了static数组,而不是不可能的const,它可以工作: public static string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };


1
只需const从OP示例中删除也可以,但是(或您的答案)可以更改Titles实例和任何值。那么这个答案有什么意义呢?
Sinatr

@Sinatr,我三年前在C#工作时回答了这个问题。我离开了,现在进入Java世界。也许我忘了补充readonly
ALZ

经过深思熟虑,您的答案是直接如何使OP代码正常工作,而没有任何const/ readonly注意事项,只需使其工作即可(例如,如果const是语法错误)。对于某些人来说,这似乎是一个有价值的答案(也许他们也试图const错误地使用它?)。
锡纳特尔

5

您可以采用另一种方法:定义一个常量字符串来表示您的数组,然后在需要时将其拆分为一个数组,例如

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

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


8
我认为执行拆分的成本远远超过了定义const所带来的任何好处。但是+1是一种独特的方法,可以跳出框框思考!;)
Radderz 2015年

我将要发布相同的解决方案,然后看到这一点,这与苛刻和否定的评论相反,这实际上非常适合我的情况,我需要将const传递给Attribute,然后将属性构造函数中的值拆分为得到我所需要的。由于没有为每个实例创建属性,所以我看不出为什么会有性能损失。
卡佩什·波帕特


3

这是一种执行所需操作的方法:

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

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

这与执行只读数组非常相似。


8
你可以这样做public static readonly ReadOnlyCollection<String> Titles = new List<String> { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();; 如果您将其设为ReadOnlyCollection,则无需在每次检索时都重新创建列表。
Nyerguds

3

这是唯一正确的答案。您目前无法执行此操作。

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

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



2

数组可能是只能在运行时评估的事物之一。常量必须在编译时求值。尝试使用“只读”而不是“常量”。


0

作为替代方案,要解决带有只读数组的元素可能被修改的问题,可以改用static属性。(单个元素仍然可以更改,但是这些更改将仅在数组的本地副本上进行。)

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

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


0

最佳选择:

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 };
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.