声明一个const数组

Jai*_*Oro 421 .net c# arrays const readonly

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

public const string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
Run Code Online (Sandbox Code Playgroud)

Cod*_*ray 639

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

public static readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
Run Code Online (Sandbox Code Playgroud)

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

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

根据您最终想要实现的目标,您可能还会考虑声明枚举:

public enum Titles { German, Spanish, Corrects, Wrongs };
Run Code Online (Sandbox Code Playgroud)

  • 注意,*array*here当然不是readonly; 标题[2] = "威尔士"; 在运行时会运行得很好 (108认同)
  • 你可能也希望它也是静态的 (44认同)
  • 抱歉投反对票,但 const 也意味着静态。将数组声明为只读并不接近解决方法。它需要是“只读静态”才能与请求的语义有任何相似之处。 (16认同)
  • 如何在方法体中声明"const"数组,而不是在类1中? (4认同)
  • 当您绝对需要一个常量时,这将无济于事,例如 switch 语句的情况。 (2认同)
  • string不是char [],请参阅[源代码](http://referencesource.microsoft.com/#mscorlib/system/string.cs).字符串可以是C#中的consts,因为编译器将用实际的字符串文字本身替换对const的所有引用. (2认同)
  • 我认为`static`部分本身是显而易见的,而不是解决方案不可或缺的一部分.我认为访问这个答案的人并不是这样; 这肯定是一个受欢迎的.拥有它肯定没有坏处.不知道为什么有人没有编辑它... (2认同)
  • @Anton,你和你的"粉丝"被删除了吗?对我来说,静态`不需要使它工作,它只是增加了引用`标题`而没有实例的可能性,但是删除了为不同实例改变值的可能性(例如,你可以使用参数的构造函数取决于你改变的值那个`readonly`字段). (2认同)

Bra*_*mir 53

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

public readonly string[] Titles = { "German", "Spanish", "Corrects", "Wrongs" };
...
Titles[0] = "bla";
Run Code Online (Sandbox Code Playgroud)

考虑使用enum,如Cody建议的那样,或IList.

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

  • 在.NET 4.5及更高版本中,您可以将列表声明为IReadOnlyList <string>而不是IList <string>. (26认同)
  • 需要明确的是,您仍然可以更改 IReadOnlyList 中的值(只是不能添加或删除元素)。但是,是的,将其声明为 IReadOnlyList 会比 IList 更好。 (3认同)

JAi*_*iro 45

您不能创建'const'数组,因为数组是对象,只能在运行时创建,const实体在编译时解析.

你可以做的是将你的数组声明为"只读".除了可以在运行时设置值之外,这与const具有相同的效果.它只能设置一次,然后是readonly(即const)值.

  • 可以声明一个常量数组;问题是用一个常量值初始化它。我想到的唯一有效的例子是“const int[] a = null;”,它不是很有用,但确实是一个数组常量的实例。 (4认同)
  • 这篇文章强调了为什么数组不能被声明为常量 (3认同)

Rog*_*ill 23

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

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

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

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

这就是为什么制作不可变数组有点棘手的部分原因。您可以编写一个包装数组的类,并且只允许通过返回副本来读取它,但它实际上不再只是一个数组。

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

要真正获得 const 数组,需要对 C# 和 MSIL 底层代码进行更新,以允许从数组中读取,但不允许写入。


mje*_*son 12

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

public static string[] Titles => new string[] { "German", "Spanish", "Corrects", "Wrongs" };
Run Code Online (Sandbox Code Playgroud)

另请参阅:C#:新增和改进的C#6.0(特别是"表达身体功能和属性"一章)

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

为了澄清,此代码与(或实际上是简写)相同:

public static string[] Titles
{
    get { return new string[] { "German", "Spanish", "Corrects", "Wrongs" }; }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这种方法有一个缺点:新的数组实际上是在每个引用上实例化的,所以如果你使用的是非常大的数组,这可能不是最有效的解决方案.但是如果你重新使用相同的数组(例如通过将它放在私有属性中),它将再次打开更改数组内容的可能性.

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

public static IReadOnlyList<string> Titles { get; } = new string[] { "German", "Spanish", "Corrects", "Wrongs" };
Run Code Online (Sandbox Code Playgroud)

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

((string[]) Titles)[1] = "French";
Run Code Online (Sandbox Code Playgroud)

  • 在这种情况下使用财产而不是字段的利润是多少? (4认同)
  • 第一种方法还有另一个缺点:例如,如果您分配给“Titles[0]”,则不会出现编译错误 - 实际上,分配尝试被悄悄地忽略了。再加上每次都重新创建数组的效率低下,我想知道这种方法是否值得展示。相比之下,第二种方法是有效的,你必须竭尽全力击败不变性。 (3认同)
  • 字段无法在每次调用时返回新对象.财产基本上是一种"伪装的功能". (2认同)

小智 8

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

public readonly static ImmutableArray<string> Tiles = ImmutableArray.Create(new[] { "German", "Spanish", "Corrects", "Wrongs" });
Run Code Online (Sandbox Code Playgroud)

需要 System.Collections.Immutable NuGet 参考

https://msdn.microsoft.com/en-us/library/mt452182(v=vs.111).aspx


Ala*_*air 6

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

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

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

阿拉斯泰尔

  • 我认为执行拆分的成本远远超过定义const所带来的任何好处.但+1是一种独特的方法,并在盒子外思考!;) (6认同)
  • 我正要发布相同的解决方案,然后看到了这个,与严厉和负面的评论相反,这实际上非常适合我的场景,我需要将 const 传递给属性,然后我将属性构造函数中的值拆分为得到我需要的东西。我认为没有理由会产生性能成本,因为没有为每个实例创建属性。 (2认同)

Ric*_*ide 6

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

public readonly IReadOnlyList<string> Titles = new [] {"German", "Spanish", "Corrects", "Wrongs" };
Run Code Online (Sandbox Code Playgroud)

适用于.NET 4.5及更高版本.


ska*_*kaz 5

我相信你只能将其设置为只读。


ALZ*_*ALZ 5

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

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

tdb*_*ett 5

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

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

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

它与做一个只读数组非常相似。

  • 你可以这样做`public static readonly ReadOnlyCollection&lt;String&gt; Titles = new List&lt;String&gt; { "German", "Spanish", "Corrects", "Wrongs" }.AsReadOnly();`; 如果您将其设为 ReadOnlyCollection,则无需在每次检索时重新创建列表。 (9认同)

mkl*_*nt0 5

一个.NET框架V4.5 +解决方案是提高了tdbeckett的回答

using System.Collections.ObjectModel;

// ...

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

注意:鉴于集合在概念上是恒定的,因此static使其在级别进行声明可能很有意义。

以上:

  • 使用数组一次初始化属性的隐式后备字段

    • 注意{ get; }-即,仅声明属性getter-是使属性本身隐式只读的原因(尝试readonly与之合并{ get; }实际上是语法错误)。

    • 或者,您可以省略{ get; }和添加readonly以创建字段而不是属性,如在问题中所示,但是将公共数据成员公开为属性而不是字段是一种良好的习惯。

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

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